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
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs one statement of the cursor protocol. `internalAll` for every caller so far: the cursor holds
|
|
3
|
+
* a connection's session state, so it must be the querier's own connection, and it must not go
|
|
4
|
+
* through `all()`, whose `serialize` is not re-entrant.
|
|
5
|
+
*/
|
|
6
|
+
export type CursorExecutor<T> = (query: string, values?: unknown[]) => Promise<T[]>;
|
|
7
|
+
/**
|
|
8
|
+
* Stream a Postgres-wire result through a server-side cursor, for a driver whose client exposes none:
|
|
9
|
+
* `bun:sql` (no cursor API at all, [oven-sh/bun#17181](https://github.com/oven-sh/bun/issues/17181))
|
|
10
|
+
* and PGlite. `pg` has `pg-query-stream` and keeps using it.
|
|
11
|
+
*
|
|
12
|
+
* `DECLARE` is only legal inside a transaction, so one is opened here when the caller has none - and
|
|
13
|
+
* then committed, or rolled back if the stream failed. That `BEGIN` goes straight to the connection
|
|
14
|
+
* rather than through `beginTransaction`, so the querier's own transaction state stays untouched:
|
|
15
|
+
* this one is the generator's, and ends with it.
|
|
16
|
+
*
|
|
17
|
+
* The cleanup lives in `finally` because a consumer that stops early (`break`, a `throw` downstream)
|
|
18
|
+
* ends the generator there and nowhere else, and an abandoned cursor holds its transaction open.
|
|
19
|
+
*/
|
|
20
|
+
export declare function streamViaCursor<T>(exec: CursorExecutor<T>, query: string, values?: unknown[], inTransaction?: boolean): AsyncIterable<T>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** The cursor a stream declares. Suffixed per call, so two streams on one connection cannot collide. */
|
|
2
|
+
const CURSOR_ALIAS = '_uql_cursor';
|
|
3
|
+
/** Rows per round trip, matching `pg-query-stream`'s own default so both paths read the same. */
|
|
4
|
+
const FETCH_SIZE = 100;
|
|
5
|
+
let cursorSeq = 0;
|
|
6
|
+
/**
|
|
7
|
+
* Stream a Postgres-wire result through a server-side cursor, for a driver whose client exposes none:
|
|
8
|
+
* `bun:sql` (no cursor API at all, [oven-sh/bun#17181](https://github.com/oven-sh/bun/issues/17181))
|
|
9
|
+
* and PGlite. `pg` has `pg-query-stream` and keeps using it.
|
|
10
|
+
*
|
|
11
|
+
* `DECLARE` is only legal inside a transaction, so one is opened here when the caller has none - and
|
|
12
|
+
* then committed, or rolled back if the stream failed. That `BEGIN` goes straight to the connection
|
|
13
|
+
* rather than through `beginTransaction`, so the querier's own transaction state stays untouched:
|
|
14
|
+
* this one is the generator's, and ends with it.
|
|
15
|
+
*
|
|
16
|
+
* The cleanup lives in `finally` because a consumer that stops early (`break`, a `throw` downstream)
|
|
17
|
+
* ends the generator there and nowhere else, and an abandoned cursor holds its transaction open.
|
|
18
|
+
*/
|
|
19
|
+
export async function* streamViaCursor(exec, query, values, inTransaction = false) {
|
|
20
|
+
const cursor = `${CURSOR_ALIAS}_${++cursorSeq}`;
|
|
21
|
+
const ownsTransaction = !inTransaction;
|
|
22
|
+
if (ownsTransaction) {
|
|
23
|
+
await exec('BEGIN');
|
|
24
|
+
}
|
|
25
|
+
let failed = false;
|
|
26
|
+
try {
|
|
27
|
+
await exec(`DECLARE ${cursor} CURSOR FOR ${query}`, values);
|
|
28
|
+
for (;;) {
|
|
29
|
+
const rows = await exec(`FETCH FORWARD ${FETCH_SIZE} FROM ${cursor}`);
|
|
30
|
+
yield* rows;
|
|
31
|
+
if (rows.length < FETCH_SIZE) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
failed = true;
|
|
38
|
+
throw err;
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
// Best-effort once the stream has failed: an error raised here would replace the one that brought
|
|
42
|
+
// us here, which is the one worth reporting, and the rollback discards the cursor either way.
|
|
43
|
+
const end = failed ? (sql) => exec(sql).catch(() => []) : exec;
|
|
44
|
+
await end(`CLOSE ${cursor}`);
|
|
45
|
+
if (ownsTransaction) {
|
|
46
|
+
await end(failed ? 'ROLLBACK' : 'COMMIT');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -4,7 +4,7 @@ import { PostgresDialect } from './postgresDialect.js';
|
|
|
4
4
|
*
|
|
5
5
|
* @remarks Uses base {@link PostgresDialect} capabilities: native JS arrays for `ANY` / `ALL`
|
|
6
6
|
* (`nativeArrays: true`) and `$n::jsonb` without a text re-cast. Bun SQL Postgres needs
|
|
7
|
-
* `
|
|
7
|
+
* `BunSqlQuerierPool` from `uql-orm/bunSql` instead (wire array literals + text json cast).
|
|
8
8
|
*/
|
|
9
9
|
export declare class PgDialect extends PostgresDialect {
|
|
10
10
|
}
|
|
@@ -4,7 +4,7 @@ import { PostgresDialect } from './postgresDialect.js';
|
|
|
4
4
|
*
|
|
5
5
|
* @remarks Uses base {@link PostgresDialect} capabilities: native JS arrays for `ANY` / `ALL`
|
|
6
6
|
* (`nativeArrays: true`) and `$n::jsonb` without a text re-cast. Bun SQL Postgres needs
|
|
7
|
-
* `
|
|
7
|
+
* `BunSqlQuerierPool` from `uql-orm/bunSql` instead (wire array literals + text json cast).
|
|
8
8
|
*/
|
|
9
9
|
export class PgDialect extends PostgresDialect {
|
|
10
10
|
}
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* arrays
|
|
4
|
-
* `
|
|
2
|
+
* Driver-shaped parameter handling for **Bun SQL** (and any client like it) on a Postgres-wire
|
|
3
|
+
* dialect: arrays go as string literals (`nativeArrays: false`, {@link PgLikeSqlDialect}'s `toPgArray`
|
|
4
|
+
* path) and a JSON bind is re-cast through text (`explicitJsonCast: true`). Both are measured, on a
|
|
5
|
+
* live server: `bun:sql` binds neither `sql.array(...)` nor a plain JS array through `unsafe()`
|
|
6
|
+
* (verified again on Bun 1.4.2), and without the text re-cast a `$set`/`$push` on a JSONB column
|
|
7
|
+
* silently writes the wrong value or throws - on Postgres and, identically, on CockroachDB, which
|
|
8
|
+
* `bun:sql` reaches through its own Postgres wire implementation.
|
|
5
9
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* implementation (see `bunSql.util.ts#normalizeBunOpts`), so it needs the identical fix: verified
|
|
11
|
-
* directly that without it, `$set`/`$push` on a JSONB column silently produce the wrong value
|
|
12
|
-
* or throw on a live CockroachDB instance.
|
|
10
|
+
* The pair is one constant because it is one driver's shape, and `BunSqlQuerierPool` hands it to
|
|
11
|
+
* `PostgresDialect`/`CockroachDialect` as their `driverCapabilities` rather than subclassing either:
|
|
12
|
+
* Bun changes how a parameter binds, never the SQL. `PgDialect` uses neither, keeping the base
|
|
13
|
+
* {@link PgLikeSqlDialect} defaults, since node-`pg` needs no fix.
|
|
13
14
|
*
|
|
14
15
|
* @remarks Optional import for custom pools. Neon uses its own serverless driver (not `bun:sql`),
|
|
15
16
|
* so `NeonDialect` is a separate, unverified case - do not assume it needs this without testing.
|
|
16
17
|
*/
|
|
17
18
|
export declare const POSTGRES_WIRE_DRIVER_CAPABILITIES: {
|
|
18
19
|
readonly nativeArrays: false;
|
|
20
|
+
readonly explicitJsonCast: true;
|
|
19
21
|
};
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* arrays
|
|
4
|
-
* `
|
|
2
|
+
* Driver-shaped parameter handling for **Bun SQL** (and any client like it) on a Postgres-wire
|
|
3
|
+
* dialect: arrays go as string literals (`nativeArrays: false`, {@link PgLikeSqlDialect}'s `toPgArray`
|
|
4
|
+
* path) and a JSON bind is re-cast through text (`explicitJsonCast: true`). Both are measured, on a
|
|
5
|
+
* live server: `bun:sql` binds neither `sql.array(...)` nor a plain JS array through `unsafe()`
|
|
6
|
+
* (verified again on Bun 1.4.2), and without the text re-cast a `$set`/`$push` on a JSONB column
|
|
7
|
+
* silently writes the wrong value or throws - on Postgres and, identically, on CockroachDB, which
|
|
8
|
+
* `bun:sql` reaches through its own Postgres wire implementation.
|
|
5
9
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* implementation (see `bunSql.util.ts#normalizeBunOpts`), so it needs the identical fix: verified
|
|
11
|
-
* directly that without it, `$set`/`$push` on a JSONB column silently produce the wrong value
|
|
12
|
-
* or throw on a live CockroachDB instance.
|
|
10
|
+
* The pair is one constant because it is one driver's shape, and `BunSqlQuerierPool` hands it to
|
|
11
|
+
* `PostgresDialect`/`CockroachDialect` as their `driverCapabilities` rather than subclassing either:
|
|
12
|
+
* Bun changes how a parameter binds, never the SQL. `PgDialect` uses neither, keeping the base
|
|
13
|
+
* {@link PgLikeSqlDialect} defaults, since node-`pg` needs no fix.
|
|
13
14
|
*
|
|
14
15
|
* @remarks Optional import for custom pools. Neon uses its own serverless driver (not `bun:sql`),
|
|
15
16
|
* so `NeonDialect` is a separate, unverified case - do not assume it needs this without testing.
|
|
16
17
|
*/
|
|
17
18
|
export const POSTGRES_WIRE_DRIVER_CAPABILITIES = {
|
|
18
19
|
nativeArrays: false,
|
|
20
|
+
explicitJsonCast: true,
|
|
19
21
|
};
|
|
@@ -44,6 +44,9 @@ const SQL_TO_CANONICAL = {
|
|
|
44
44
|
char: { category: 'string' },
|
|
45
45
|
character: { category: 'string' },
|
|
46
46
|
varchar: { category: 'string' },
|
|
47
|
+
varchar2: { category: 'string' },
|
|
48
|
+
nvarchar: { category: 'string' },
|
|
49
|
+
nchar: { category: 'string' },
|
|
47
50
|
'character varying': { category: 'string' },
|
|
48
51
|
text: { category: 'string', size: 'small' },
|
|
49
52
|
tinytext: { category: 'string', size: 'tiny' },
|
|
@@ -64,11 +67,15 @@ const SQL_TO_CANONICAL = {
|
|
|
64
67
|
'timestamp with time zone': { category: 'timestamp', withTimezone: true },
|
|
65
68
|
timestamptz: { category: 'timestamp', withTimezone: true },
|
|
66
69
|
datetime: { category: 'timestamp' },
|
|
70
|
+
datetime2: { category: 'timestamp' },
|
|
71
|
+
smalldatetime: { category: 'timestamp' },
|
|
72
|
+
datetimeoffset: { category: 'timestamp', withTimezone: true },
|
|
67
73
|
// === JSON ===
|
|
68
74
|
json: { category: 'json' },
|
|
69
75
|
jsonb: { category: 'json' },
|
|
70
76
|
// === UUID ===
|
|
71
77
|
uuid: { category: 'uuid' },
|
|
78
|
+
uniqueidentifier: { category: 'uuid' },
|
|
72
79
|
// === Binary ===
|
|
73
80
|
blob: { category: 'blob' },
|
|
74
81
|
bytea: { category: 'blob' },
|
|
@@ -77,15 +84,16 @@ const SQL_TO_CANONICAL = {
|
|
|
77
84
|
tinyblob: { category: 'blob', size: 'tiny' },
|
|
78
85
|
mediumblob: { category: 'blob', size: 'medium' },
|
|
79
86
|
longblob: { category: 'blob', size: 'big' },
|
|
87
|
+
image: { category: 'blob', size: 'big' },
|
|
80
88
|
// === Vector (for AI/embeddings) ===
|
|
81
89
|
vector: { category: 'vector' },
|
|
82
90
|
halfvec: { category: 'halfvec' },
|
|
83
91
|
sparsevec: { category: 'sparsevec' },
|
|
84
92
|
};
|
|
85
93
|
/**
|
|
86
|
-
* pgvector is the only engine with three vector column types, so every other
|
|
87
|
-
* canonical categories onto the single type it does have (see `hasNarrowVectorTypes` on the dialect,
|
|
88
|
-
* dialect-side half of the same fact).
|
|
94
|
+
* pgvector is the only engine with three vector column types, so every other engine maps all three
|
|
95
|
+
* canonical categories onto the single type it does have (see `hasNarrowVectorTypes` on the dialect,
|
|
96
|
+
* the dialect-side half of the same fact).
|
|
89
97
|
*/
|
|
90
98
|
function withVectorType(scalars, vector) {
|
|
91
99
|
return { ...scalars, vector, halfvec: vector, sparsevec: vector };
|
|
@@ -103,11 +111,9 @@ const PG_SCALAR_MAP = {
|
|
|
103
111
|
uuid: 'UUID',
|
|
104
112
|
blob: 'BYTEA',
|
|
105
113
|
};
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
halfvec: 'HALFVEC',
|
|
110
|
-
sparsevec: 'SPARSEVEC',
|
|
114
|
+
const PG_SIZES = {
|
|
115
|
+
integer: { tiny: 'SMALLINT', small: 'SMALLINT', medium: 'INTEGER', big: 'BIGINT' },
|
|
116
|
+
float: { tiny: 'REAL', small: 'REAL', medium: 'DOUBLE PRECISION', big: 'DOUBLE PRECISION' },
|
|
111
117
|
};
|
|
112
118
|
const MYSQL_SCALAR_MAP = {
|
|
113
119
|
integer: 'INT',
|
|
@@ -122,6 +128,13 @@ const MYSQL_SCALAR_MAP = {
|
|
|
122
128
|
uuid: 'CHAR(36)',
|
|
123
129
|
blob: 'BLOB',
|
|
124
130
|
};
|
|
131
|
+
/** MariaDB is a MySQL fork and spells every one of these the same way, so both take the one map. */
|
|
132
|
+
const MYSQL_SIZES = {
|
|
133
|
+
integer: { tiny: 'TINYINT', small: 'SMALLINT', medium: 'MEDIUMINT', big: 'BIGINT' },
|
|
134
|
+
float: { tiny: 'FLOAT', small: 'FLOAT', medium: 'DOUBLE', big: 'DOUBLE' },
|
|
135
|
+
string: { tiny: 'TINYTEXT', small: 'TEXT', medium: 'MEDIUMTEXT', big: 'LONGTEXT' },
|
|
136
|
+
blob: { tiny: 'TINYBLOB', small: 'BLOB', medium: 'MEDIUMBLOB', big: 'LONGBLOB' },
|
|
137
|
+
};
|
|
125
138
|
const SQLITE_SCALAR_MAP = {
|
|
126
139
|
integer: 'INTEGER',
|
|
127
140
|
float: 'REAL',
|
|
@@ -135,6 +148,35 @@ const SQLITE_SCALAR_MAP = {
|
|
|
135
148
|
uuid: 'TEXT',
|
|
136
149
|
blob: 'BLOB',
|
|
137
150
|
};
|
|
151
|
+
/**
|
|
152
|
+
* Every string column is `NVARCHAR`: `VARCHAR` is a codepage type on SQL Server and silently destroys
|
|
153
|
+
* anything outside it on write. UQL never exposes the choice, so there is nothing to weigh per column.
|
|
154
|
+
*/
|
|
155
|
+
const MSSQL_SCALAR_MAP = {
|
|
156
|
+
integer: 'INT',
|
|
157
|
+
float: 'REAL',
|
|
158
|
+
decimal: 'DECIMAL',
|
|
159
|
+
string: 'NVARCHAR',
|
|
160
|
+
boolean: 'BIT',
|
|
161
|
+
date: 'DATE',
|
|
162
|
+
time: 'TIME',
|
|
163
|
+
timestamp: 'DATETIME2',
|
|
164
|
+
json: 'NVARCHAR(MAX)',
|
|
165
|
+
uuid: 'UNIQUEIDENTIFIER',
|
|
166
|
+
blob: 'VARBINARY(MAX)',
|
|
167
|
+
};
|
|
168
|
+
/** `FLOAT` is eight bytes here and `REAL` four - the opposite of the MySQL family's spelling. */
|
|
169
|
+
const MSSQL_SIZES = {
|
|
170
|
+
integer: { tiny: 'TINYINT', small: 'SMALLINT', medium: 'INT', big: 'BIGINT' },
|
|
171
|
+
float: { tiny: 'REAL', small: 'REAL', medium: 'FLOAT', big: 'FLOAT' },
|
|
172
|
+
string: { tiny: 'NVARCHAR(255)', small: 'NVARCHAR(MAX)', medium: 'NVARCHAR(MAX)', big: 'NVARCHAR(MAX)' },
|
|
173
|
+
blob: {
|
|
174
|
+
tiny: 'VARBINARY(255)',
|
|
175
|
+
small: 'VARBINARY(MAX)',
|
|
176
|
+
medium: 'VARBINARY(MAX)',
|
|
177
|
+
big: 'VARBINARY(MAX)',
|
|
178
|
+
},
|
|
179
|
+
};
|
|
138
180
|
/** MongoDB uses BSON types, not SQL types. These are placeholders for compatibility. */
|
|
139
181
|
const MONGO_SCALAR_MAP = {
|
|
140
182
|
integer: 'int',
|
|
@@ -149,73 +191,31 @@ const MONGO_SCALAR_MAP = {
|
|
|
149
191
|
uuid: 'binData',
|
|
150
192
|
blob: 'binData',
|
|
151
193
|
};
|
|
152
|
-
/** Every engine's
|
|
153
|
-
const
|
|
154
|
-
postgres:
|
|
194
|
+
/** Every engine's spelling of the canonical types. A new engine is one entry here and nothing else. */
|
|
195
|
+
const ENGINE_TYPES = {
|
|
196
|
+
postgres: {
|
|
197
|
+
scalars: { ...PG_SCALAR_MAP, vector: 'VECTOR', halfvec: 'HALFVEC', sparsevec: 'SPARSEVEC' },
|
|
198
|
+
sizes: PG_SIZES,
|
|
199
|
+
},
|
|
155
200
|
// CockroachDB's VECTOR is native, no extension needed.
|
|
156
|
-
cockroachdb: withVectorType(PG_SCALAR_MAP, 'VECTOR'),
|
|
201
|
+
cockroachdb: { scalars: withVectorType(PG_SCALAR_MAP, 'VECTOR'), sizes: PG_SIZES },
|
|
157
202
|
// MySQL does have a `VECTOR` type (26.7), but no distance function outside HeatWave and no vector
|
|
158
203
|
// index, so JSON keeps the column queryable with the JSON operators and needs no conversion.
|
|
159
|
-
mysql:
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
};
|
|
164
|
-
/**
|
|
165
|
-
* Size variant modifiers for SQL types.
|
|
166
|
-
*/
|
|
167
|
-
const PG_SIZE_MODIFIERS = {
|
|
168
|
-
integer: {
|
|
169
|
-
tiny: 'SMALLINT',
|
|
170
|
-
small: 'SMALLINT',
|
|
171
|
-
medium: 'INTEGER',
|
|
172
|
-
big: 'BIGINT',
|
|
173
|
-
},
|
|
174
|
-
float: {
|
|
175
|
-
tiny: 'REAL',
|
|
176
|
-
small: 'REAL',
|
|
177
|
-
medium: 'DOUBLE PRECISION',
|
|
178
|
-
big: 'DOUBLE PRECISION',
|
|
179
|
-
},
|
|
180
|
-
};
|
|
181
|
-
/**
|
|
182
|
-
* MariaDB is a MySQL fork and spells every one of these the same way, so both take the one map.
|
|
183
|
-
* Listed per-dialect, MariaDB's had only `integer`: a `double` column was created `FLOAT` there, four
|
|
184
|
-
* bytes where the entity asked for eight, and a `big` string or blob lost its `LONG` prefix.
|
|
185
|
-
*/
|
|
186
|
-
const MYSQL_SIZE_MODIFIERS = {
|
|
187
|
-
integer: {
|
|
188
|
-
tiny: 'TINYINT',
|
|
189
|
-
small: 'SMALLINT',
|
|
190
|
-
medium: 'MEDIUMINT',
|
|
191
|
-
big: 'BIGINT',
|
|
204
|
+
mysql: {
|
|
205
|
+
scalars: withVectorType(MYSQL_SCALAR_MAP, 'JSON'),
|
|
206
|
+
sizes: MYSQL_SIZES,
|
|
207
|
+
decimal: { precision: 10, scale: 2 },
|
|
192
208
|
},
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
big: 'DOUBLE',
|
|
209
|
+
mariadb: {
|
|
210
|
+
scalars: withVectorType(MYSQL_SCALAR_MAP, 'VECTOR'),
|
|
211
|
+
sizes: MYSQL_SIZES,
|
|
212
|
+
decimal: { precision: 10, scale: 2 },
|
|
198
213
|
},
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
},
|
|
205
|
-
blob: {
|
|
206
|
-
tiny: 'TINYBLOB',
|
|
207
|
-
small: 'BLOB',
|
|
208
|
-
medium: 'MEDIUMBLOB',
|
|
209
|
-
big: 'LONGBLOB',
|
|
210
|
-
},
|
|
211
|
-
};
|
|
212
|
-
const SIZE_MODIFIERS = {
|
|
213
|
-
postgres: PG_SIZE_MODIFIERS,
|
|
214
|
-
cockroachdb: PG_SIZE_MODIFIERS,
|
|
215
|
-
mysql: MYSQL_SIZE_MODIFIERS,
|
|
216
|
-
sqlite: {}, // SQLite uses affinity, no size modifiers
|
|
217
|
-
mariadb: MYSQL_SIZE_MODIFIERS,
|
|
218
|
-
mongodb: {},
|
|
214
|
+
// SQLite uses affinity, so no size variants.
|
|
215
|
+
sqlite: { scalars: withVectorType(SQLITE_SCALAR_MAP, 'TEXT') },
|
|
216
|
+
// A 2025 server has a native `VECTOR`; below that the column is JSON text, which stays queryable.
|
|
217
|
+
mssql: { scalars: withVectorType(MSSQL_SCALAR_MAP, 'NVARCHAR(MAX)'), sizes: MSSQL_SIZES },
|
|
218
|
+
mongodb: { scalars: withVectorType(MONGO_SCALAR_MAP, 'array') },
|
|
219
219
|
};
|
|
220
220
|
/**
|
|
221
221
|
* Maps canonical types to TypeScript types for entity generation.
|
|
@@ -317,61 +317,44 @@ export function canonicalColumnType(sqlType, reported = {}) {
|
|
|
317
317
|
export function canonicalToSql(type, dialect) {
|
|
318
318
|
if (type.raw)
|
|
319
319
|
return type.raw;
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
320
|
+
const engine = ENGINE_TYPES[dialect.dialectName];
|
|
321
|
+
const { features } = dialect;
|
|
322
|
+
// A `size` the engine spells out wins outright; anything else falls through to the rules below.
|
|
323
|
+
// `TEXT` canonicalizes to `size: 'small'`, so a string counts as unsized only where the engine
|
|
324
|
+
// declares no variant for it, which is how Postgres reaches `TEXT` rather than its base `VARCHAR`.
|
|
325
|
+
const sized = engine.sizes?.[type.category]?.[type.size];
|
|
326
|
+
let sqlType = sized ?? engine.scalars[type.category];
|
|
327
|
+
if (type.category === 'string' && !sized) {
|
|
328
|
+
sqlType = formatStringSqlType(type, engine.scalars.string, features.stringSizing);
|
|
324
329
|
}
|
|
325
330
|
else if (type.category === 'decimal') {
|
|
326
|
-
sqlType = formatDecimalSqlType(type,
|
|
331
|
+
sqlType = formatDecimalSqlType(type, engine.decimal, sqlType);
|
|
327
332
|
}
|
|
328
|
-
else if (isVectorCategory(type.category) && type.length &&
|
|
333
|
+
else if (isVectorCategory(type.category) && type.length && features.vectorSupportsLength) {
|
|
329
334
|
sqlType = `${sqlType}(${type.length})`;
|
|
330
335
|
}
|
|
331
|
-
if (type.category === 'timestamp' && type.withTimezone &&
|
|
336
|
+
if (type.category === 'timestamp' && type.withTimezone && features.supportsTimestamptz) {
|
|
332
337
|
sqlType = 'TIMESTAMPTZ';
|
|
333
338
|
}
|
|
334
|
-
|
|
335
|
-
sqlType = `${sqlType} UNSIGNED`;
|
|
336
|
-
}
|
|
337
|
-
return sqlType;
|
|
339
|
+
return type.unsigned && features.supportsUnsigned ? `${sqlType} UNSIGNED` : sqlType;
|
|
338
340
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
if (
|
|
342
|
-
|
|
343
|
-
if (sizeMap?.[type.size]) {
|
|
344
|
-
sqlType = sizeMap[type.size];
|
|
345
|
-
}
|
|
341
|
+
/** See {@link EngineFeatures.stringSizing} for what each mode means. */
|
|
342
|
+
function formatStringSqlType(type, base, sizing) {
|
|
343
|
+
if (sizing === 'text') {
|
|
344
|
+
return base;
|
|
346
345
|
}
|
|
347
|
-
|
|
348
|
-
}
|
|
349
|
-
function formatStringSqlType(type, dialect) {
|
|
350
|
-
const { dialectName, features } = dialect;
|
|
351
|
-
if (dialectName === 'sqlite')
|
|
352
|
-
return 'TEXT';
|
|
353
|
-
if (features.defaultStringAsText)
|
|
354
|
-
return type.length ? `VARCHAR(${type.length})` : 'TEXT';
|
|
355
|
-
if (dialectName === 'mysql' || dialectName === 'mariadb') {
|
|
356
|
-
if (type.size === 'tiny')
|
|
357
|
-
return 'TINYTEXT';
|
|
358
|
-
if (type.size === 'small')
|
|
359
|
-
return 'TEXT';
|
|
360
|
-
if (type.size === 'medium')
|
|
361
|
-
return 'MEDIUMTEXT';
|
|
362
|
-
if (type.size === 'big')
|
|
363
|
-
return 'LONGTEXT';
|
|
364
|
-
return type.length ? `VARCHAR(${type.length})` : 'VARCHAR(255)';
|
|
346
|
+
if (type.length) {
|
|
347
|
+
return `${base}(${type.length})`;
|
|
365
348
|
}
|
|
366
|
-
return
|
|
349
|
+
return sizing === 'bounded-text' ? 'TEXT' : `${base}(255)`;
|
|
367
350
|
}
|
|
368
|
-
function formatDecimalSqlType(type,
|
|
369
|
-
const p = type.precision ??
|
|
370
|
-
const s = type.scale ??
|
|
371
|
-
if (p
|
|
372
|
-
return
|
|
351
|
+
function formatDecimalSqlType(type, fallback, baseType) {
|
|
352
|
+
const p = type.precision ?? fallback?.precision;
|
|
353
|
+
const s = type.scale ?? fallback?.scale;
|
|
354
|
+
if (p === undefined) {
|
|
355
|
+
return baseType;
|
|
373
356
|
}
|
|
374
|
-
return baseType
|
|
357
|
+
return s === undefined ? `${baseType}(${p})` : `${baseType}(${p}, ${s})`;
|
|
375
358
|
}
|
|
376
359
|
/**
|
|
377
360
|
* Convert a canonical type to a TypeScript type string.
|
|
@@ -19,7 +19,10 @@ export class SqliteDialect extends AbstractSqlDialect {
|
|
|
19
19
|
vectorIndexRequiresNotNull: false,
|
|
20
20
|
vectorSupportsLength: false,
|
|
21
21
|
supportsTimestamptz: false,
|
|
22
|
-
|
|
22
|
+
stringSizing: 'text',
|
|
23
|
+
supportsUnsigned: false,
|
|
24
|
+
multipleCascadePaths: true,
|
|
25
|
+
serverSideCursors: false,
|
|
23
26
|
};
|
|
24
27
|
dialectName = 'sqlite';
|
|
25
28
|
escapeIdChar = '`';
|
package/dist/type/dialect.d.ts
CHANGED
|
@@ -128,8 +128,35 @@ export interface EngineFeatures {
|
|
|
128
128
|
readonly vectorSupportsLength: boolean;
|
|
129
129
|
/** Whether the dialect natively supports the TIMESTAMPTZ alias/type. */
|
|
130
130
|
readonly supportsTimestamptz: boolean;
|
|
131
|
-
/**
|
|
132
|
-
|
|
131
|
+
/**
|
|
132
|
+
* How a `string` column is sized: `text` is always the unbounded type whatever length was asked
|
|
133
|
+
* for (SQLite, whose affinity makes a bound meaningless), `bounded-text` takes the bound when there
|
|
134
|
+
* is one and the unbounded type otherwise (the Postgres family), `varchar` is always bounded and
|
|
135
|
+
* falls back to 255 (the MySQL family, SQL Server).
|
|
136
|
+
*
|
|
137
|
+
* Three-way for the reason {@link commentSyntax} is: a boolean puts SQLite and Postgres on the
|
|
138
|
+
* same branch, and they need different answers.
|
|
139
|
+
*/
|
|
140
|
+
readonly stringSizing: 'text' | 'bounded-text' | 'varchar';
|
|
141
|
+
/** Whether the engine has unsigned integers, so `@Field({ unsigned: true })` reaches the column. */
|
|
142
|
+
readonly supportsUnsigned: boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Whether one table can be reached by two cascading foreign-key paths. SQL Server refuses the
|
|
145
|
+
* constraint outright ("may cause cycles or multiple cascade paths", error 1785) and Oracle
|
|
146
|
+
* likewise, so a cascade is downgraded to `NO ACTION` there rather than emitting DDL the engine
|
|
147
|
+
* rejects - which would otherwise fail on any diamond-shaped schema at create time.
|
|
148
|
+
*/
|
|
149
|
+
readonly multipleCascadePaths: boolean;
|
|
150
|
+
/**
|
|
151
|
+
* Whether the engine has SQL-level cursors (`DECLARE`/`FETCH FORWARD`/`CLOSE`), which is how a
|
|
152
|
+
* driver with no cursor API of its own still streams a result set instead of buffering it - see
|
|
153
|
+
* `postgres/pgCursorStream.ts`. True across the Postgres wire family, whose spelling that helper
|
|
154
|
+
* speaks; SQL Server and Oracle have cursors of their own but not this syntax.
|
|
155
|
+
*
|
|
156
|
+
* The node-`pg` family reports `true` and goes on using `pg-query-stream`: the flag says the engine
|
|
157
|
+
* has cursors, not that the driver needs them.
|
|
158
|
+
*/
|
|
159
|
+
readonly serverSideCursors: boolean;
|
|
133
160
|
}
|
|
134
161
|
export interface DialectFeatures extends EngineFeatures, DriverCapabilities {
|
|
135
162
|
}
|
|
@@ -219,13 +246,13 @@ export interface QueryDialect {
|
|
|
219
246
|
/**
|
|
220
247
|
* Supported SQL dialect identifiers.
|
|
221
248
|
*/
|
|
222
|
-
export type SqlDialectName = 'postgres' | 'cockroachdb' | 'mysql' | 'mariadb' | 'sqlite';
|
|
249
|
+
export type SqlDialectName = 'postgres' | 'cockroachdb' | 'mysql' | 'mariadb' | 'sqlite' | 'mssql';
|
|
223
250
|
/**
|
|
224
251
|
* Minimal dialect interface exposing escapeIdChar for SQL operations
|
|
225
252
|
*/
|
|
226
253
|
export interface SqlQueryDialect extends QueryDialect {
|
|
227
254
|
/**
|
|
228
|
-
* The SQL dialect name (postgres, mysql, mariadb, sqlite).
|
|
255
|
+
* The SQL dialect name (postgres, mysql, mariadb, sqlite, mssql).
|
|
229
256
|
*/
|
|
230
257
|
readonly dialectName: SqlDialectName;
|
|
231
258
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DialectName } from './querier.js';
|
|
2
|
-
declare const KNOWN_MIGRATOR_DIALECTS: readonly ["postgres", "cockroachdb", "mysql", "mariadb", "sqlite", "mongodb"];
|
|
2
|
+
declare const KNOWN_MIGRATOR_DIALECTS: readonly ["postgres", "cockroachdb", "mysql", "mariadb", "sqlite", "mssql", "mongodb"];
|
|
3
3
|
export type KnownMigratorDialect = (typeof KNOWN_MIGRATOR_DIALECTS)[number];
|
|
4
4
|
/**
|
|
5
5
|
* Whether `d` is supported by built-in migrator introspection / schema generators.
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uql-orm",
|
|
3
3
|
"homepage": "https://uql-orm.dev",
|
|
4
|
-
"description": "The JSON-native TypeScript ORM for Bun, Browsers, Edge, Deno, Node, Workers. Supports PostgreSQL, PGlite, MySQL, MariaDB, SQLite, CockroachDB, Turso, Neon, Cloudflare D1 and MongoDB. Queries are plain JSON, typed to the leaf.",
|
|
4
|
+
"description": "The JSON-native TypeScript ORM for Bun, Browsers, Edge, Deno, Node, Workers. Supports PostgreSQL, PGlite, MySQL, MariaDB, SQLite, CockroachDB, SQL Server, Turso, Neon, Cloudflare D1 and MongoDB. Queries are plain JSON, typed to the leaf.",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.52.0",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"engines": {
|
|
9
9
|
"node": ">=24"
|
|
@@ -49,7 +49,8 @@
|
|
|
49
49
|
"./pglite": "./dist/pglite/index.js",
|
|
50
50
|
"./d1": "./dist/d1/index.js",
|
|
51
51
|
"./bunSql": "./dist/bunSql/index.js",
|
|
52
|
-
"./package.json": "./package.json"
|
|
52
|
+
"./package.json": "./package.json",
|
|
53
|
+
"./mssql": "./dist/mssql/index.js"
|
|
53
54
|
},
|
|
54
55
|
"files": [
|
|
55
56
|
"dist",
|
|
@@ -75,6 +76,7 @@
|
|
|
75
76
|
"express": ">=5.0.0",
|
|
76
77
|
"mariadb": ">=3.0.0",
|
|
77
78
|
"mongodb": ">=6.0.0",
|
|
79
|
+
"mssql": ">=11.0.0",
|
|
78
80
|
"mysql2": ">=3.0.0",
|
|
79
81
|
"pg": ">=8.0.0",
|
|
80
82
|
"pg-query-stream": ">=4.0.0",
|
|
@@ -125,6 +127,9 @@
|
|
|
125
127
|
},
|
|
126
128
|
"rxjs": {
|
|
127
129
|
"optional": true
|
|
130
|
+
},
|
|
131
|
+
"mssql": {
|
|
132
|
+
"optional": true
|
|
128
133
|
}
|
|
129
134
|
},
|
|
130
135
|
"devDependencies": {
|
|
@@ -139,12 +144,14 @@
|
|
|
139
144
|
"@tursodatabase/serverless": "^1.4.0",
|
|
140
145
|
"@types/better-sqlite3": "^9.6.0",
|
|
141
146
|
"@types/express": "^5.0.6",
|
|
147
|
+
"@types/mssql": "^12.3.0",
|
|
142
148
|
"@types/pg": "^8.23.1",
|
|
143
149
|
"@types/ws": "^8.18.1",
|
|
144
150
|
"better-sqlite3": "^13.0.3",
|
|
145
151
|
"express": "^5.2.1",
|
|
146
152
|
"mariadb": "^3.5.4",
|
|
147
153
|
"mongodb": "^7.6.0",
|
|
154
|
+
"mssql": "^11",
|
|
148
155
|
"mysql2": "^3.24.4",
|
|
149
156
|
"pg": "^8.23.0",
|
|
150
157
|
"pg-query-stream": "^4.17.0",
|
|
@@ -185,6 +192,9 @@
|
|
|
185
192
|
"sqlite",
|
|
186
193
|
"sqlite3",
|
|
187
194
|
"cockroachdb",
|
|
195
|
+
"mssql",
|
|
196
|
+
"sqlserver",
|
|
197
|
+
"sql-server",
|
|
188
198
|
"mongodb",
|
|
189
199
|
"mongo",
|
|
190
200
|
"libsql",
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { CockroachDialect } from '../cockroachdb/cockroachDialect.js';
|
|
2
|
-
import type { DialectFeatures } from '../type/index.js';
|
|
3
|
-
/**
|
|
4
|
-
* CockroachDB Dialect specialization for the `bun:sql` driver, which routes CockroachDB
|
|
5
|
-
* connections through its own Postgres wire-protocol implementation (see
|
|
6
|
-
* `bunSql.util.ts#normalizeBunOpts`), so it needs the identical fix as {@link BunSqlPostgresDialect}:
|
|
7
|
-
* without it, `$set`/`$push` on a JSONB column silently produce the wrong value or throw
|
|
8
|
-
* (verified directly against a live CockroachDB instance via `bun:sql`).
|
|
9
|
-
*/
|
|
10
|
-
export declare class BunSqlCockroachDialect extends CockroachDialect {
|
|
11
|
-
protected readonly featureOverrides: Partial<DialectFeatures>;
|
|
12
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { CockroachDialect } from '../cockroachdb/cockroachDialect.js';
|
|
2
|
-
import { POSTGRES_WIRE_DRIVER_CAPABILITIES } from '../postgres/postgresWireDriverCapabilities.js';
|
|
3
|
-
/**
|
|
4
|
-
* CockroachDB Dialect specialization for the `bun:sql` driver, which routes CockroachDB
|
|
5
|
-
* connections through its own Postgres wire-protocol implementation (see
|
|
6
|
-
* `bunSql.util.ts#normalizeBunOpts`), so it needs the identical fix as {@link BunSqlPostgresDialect}:
|
|
7
|
-
* without it, `$set`/`$push` on a JSONB column silently produce the wrong value or throw
|
|
8
|
-
* (verified directly against a live CockroachDB instance via `bun:sql`).
|
|
9
|
-
*/
|
|
10
|
-
export class BunSqlCockroachDialect extends CockroachDialect {
|
|
11
|
-
featureOverrides = {
|
|
12
|
-
...POSTGRES_WIRE_DRIVER_CAPABILITIES,
|
|
13
|
-
explicitJsonCast: true,
|
|
14
|
-
};
|
|
15
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { PostgresDialect } from '../postgres/postgresDialect.js';
|
|
2
|
-
import type { DialectFeatures } from '../type/index.js';
|
|
3
|
-
/**
|
|
4
|
-
* Postgres Dialect specialization for the `bun:sql` driver.
|
|
5
|
-
*
|
|
6
|
-
* @remarks Reuses wire array encoding plus `explicitJsonCast` so JSON merge/push binds
|
|
7
|
-
* reliably; `PgDialect` omits the text re-cast.
|
|
8
|
-
*/
|
|
9
|
-
export declare class BunSqlPostgresDialect extends PostgresDialect {
|
|
10
|
-
protected readonly featureOverrides: Partial<DialectFeatures>;
|
|
11
|
-
}
|