uql-orm 0.51.0 → 0.53.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/browser/uql-browser.min.js +2 -2
- package/dist/browser/uql-browser.min.js.map +5 -5
- 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/abstractDialect.d.ts +5 -5
- package/dist/dialect/abstractDialect.js +7 -6
- package/dist/dialect/abstractSqlDialect.d.ts +59 -6
- package/dist/dialect/abstractSqlDialect.js +90 -32
- 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.d.ts +0 -1
- package/dist/dialect/mysqlLikeSqlDialect.js +3 -3
- package/dist/dialect/pgLikeSqlDialect.js +3 -2
- package/dist/dialect/vectorSqlDialect.js +2 -1
- package/dist/http/handler.js +3 -12
- package/dist/http/query.js +4 -1
- package/dist/migrate/builder/expressions.js +5 -0
- package/dist/migrate/ddl/index.d.ts +8 -0
- package/dist/migrate/ddl/index.js +11 -0
- package/dist/migrate/ddl/mssqlTableDdl.d.ts +23 -0
- package/dist/migrate/ddl/mssqlTableDdl.js +61 -0
- package/dist/migrate/ddl/tableDdl.d.ts +34 -0
- package/dist/migrate/ddl/tableDdl.js +71 -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 +60 -0
- package/dist/migrate/introspection/mssqlIntrospector.js +205 -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/migrate/schemaGenerator.d.ts +8 -10
- package/dist/migrate/schemaGenerator.js +29 -74
- package/dist/mongo/mongoDialect.js +12 -14
- package/dist/mssql/index.d.ts +3 -0
- package/dist/mssql/index.js +3 -0
- package/dist/mssql/mssqlDialect.d.ts +155 -0
- package/dist/mssql/mssqlDialect.js +341 -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/querier/abstractQuerier.js +13 -11
- package/dist/querier/abstractSqlQuerier.js +3 -3
- package/dist/schema/canonicalType.js +96 -113
- package/dist/sqlite/sqliteDialect.js +3 -2
- package/dist/type/dialect.d.ts +24 -5
- package/dist/type/migratorDialect.d.ts +1 -1
- package/dist/type/migratorDialect.js +1 -0
- package/dist/type/query.d.ts +1 -1
- package/dist/type/queryWhere.d.ts +7 -19
- package/dist/type/vector.d.ts +3 -1
- package/dist/util/dialect.util.d.ts +10 -7
- package/dist/util/dialect.util.js +16 -29
- package/dist/util/object.util.d.ts +2 -0
- package/dist/util/object.util.js +4 -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
|
@@ -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
|
+
// 2025 and up; below that the server refuses the type rather than storing it as text.
|
|
217
|
+
mssql: { scalars: withVectorType(MSSQL_SCALAR_MAP, 'VECTOR'), 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.
|
|
@@ -11,7 +11,6 @@ export class SqliteDialect extends AbstractSqlDialect {
|
|
|
11
11
|
indexIfNotExists: true,
|
|
12
12
|
schemas: false, // SQLite's namespaces are attached database files, not declared objects
|
|
13
13
|
dropTableCascade: false,
|
|
14
|
-
renameColumn: true,
|
|
15
14
|
foreignKeyAlter: false, // SQLite does not support adding FKs to existing tables
|
|
16
15
|
primaryKeyAlter: false, // nor changing a key: the only route is rebuilding the table
|
|
17
16
|
generatedColumnAdd: false, // accepted in a CREATE TABLE, rejected in an ALTER
|
|
@@ -19,7 +18,9 @@ export class SqliteDialect extends AbstractSqlDialect {
|
|
|
19
18
|
vectorIndexRequiresNotNull: false,
|
|
20
19
|
vectorSupportsLength: false,
|
|
21
20
|
supportsTimestamptz: false,
|
|
22
|
-
|
|
21
|
+
stringSizing: 'text',
|
|
22
|
+
supportsUnsigned: false,
|
|
23
|
+
serverSideCursors: false,
|
|
23
24
|
};
|
|
24
25
|
dialectName = 'sqlite';
|
|
25
26
|
escapeIdChar = '`';
|
package/dist/type/dialect.d.ts
CHANGED
|
@@ -89,7 +89,6 @@ export interface EngineFeatures {
|
|
|
89
89
|
*/
|
|
90
90
|
readonly schemas: boolean;
|
|
91
91
|
readonly dropTableCascade: boolean;
|
|
92
|
-
readonly renameColumn: boolean;
|
|
93
92
|
readonly foreignKeyAlter: boolean;
|
|
94
93
|
/**
|
|
95
94
|
* Whether a table's primary key can be changed on an existing table. False on SQLite, whose only
|
|
@@ -128,8 +127,28 @@ export interface EngineFeatures {
|
|
|
128
127
|
readonly vectorSupportsLength: boolean;
|
|
129
128
|
/** Whether the dialect natively supports the TIMESTAMPTZ alias/type. */
|
|
130
129
|
readonly supportsTimestamptz: boolean;
|
|
131
|
-
/**
|
|
132
|
-
|
|
130
|
+
/**
|
|
131
|
+
* How a `string` column is sized: `text` is always the unbounded type whatever length was asked
|
|
132
|
+
* for (SQLite, whose affinity makes a bound meaningless), `bounded-text` takes the bound when there
|
|
133
|
+
* is one and the unbounded type otherwise (the Postgres family), `varchar` is always bounded and
|
|
134
|
+
* falls back to 255 (the MySQL family, SQL Server).
|
|
135
|
+
*
|
|
136
|
+
* Three-way for the reason {@link commentSyntax} is: a boolean puts SQLite and Postgres on the
|
|
137
|
+
* same branch, and they need different answers.
|
|
138
|
+
*/
|
|
139
|
+
readonly stringSizing: 'text' | 'bounded-text' | 'varchar';
|
|
140
|
+
/** Whether the engine has unsigned integers, so `@Field({ unsigned: true })` reaches the column. */
|
|
141
|
+
readonly supportsUnsigned: boolean;
|
|
142
|
+
/**
|
|
143
|
+
* Whether the engine has SQL-level cursors (`DECLARE`/`FETCH FORWARD`/`CLOSE`), which is how a
|
|
144
|
+
* driver with no cursor API of its own still streams a result set instead of buffering it - see
|
|
145
|
+
* `postgres/pgCursorStream.ts`. True across the Postgres wire family, whose spelling that helper
|
|
146
|
+
* speaks; SQL Server and Oracle have cursors of their own but not this syntax.
|
|
147
|
+
*
|
|
148
|
+
* The node-`pg` family reports `true` and goes on using `pg-query-stream`: the flag says the engine
|
|
149
|
+
* has cursors, not that the driver needs them.
|
|
150
|
+
*/
|
|
151
|
+
readonly serverSideCursors: boolean;
|
|
133
152
|
}
|
|
134
153
|
export interface DialectFeatures extends EngineFeatures, DriverCapabilities {
|
|
135
154
|
}
|
|
@@ -219,13 +238,13 @@ export interface QueryDialect {
|
|
|
219
238
|
/**
|
|
220
239
|
* Supported SQL dialect identifiers.
|
|
221
240
|
*/
|
|
222
|
-
export type SqlDialectName = 'postgres' | 'cockroachdb' | 'mysql' | 'mariadb' | 'sqlite';
|
|
241
|
+
export type SqlDialectName = 'postgres' | 'cockroachdb' | 'mysql' | 'mariadb' | 'sqlite' | 'mssql';
|
|
223
242
|
/**
|
|
224
243
|
* Minimal dialect interface exposing escapeIdChar for SQL operations
|
|
225
244
|
*/
|
|
226
245
|
export interface SqlQueryDialect extends QueryDialect {
|
|
227
246
|
/**
|
|
228
|
-
* The SQL dialect name (postgres, mysql, mariadb, sqlite).
|
|
247
|
+
* The SQL dialect name (postgres, mysql, mariadb, sqlite, mssql).
|
|
229
248
|
*/
|
|
230
249
|
readonly dialectName: SqlDialectName;
|
|
231
250
|
/**
|
|
@@ -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/dist/type/query.d.ts
CHANGED
|
@@ -143,7 +143,7 @@ type ToOneRelationKey<E> = {
|
|
|
143
143
|
type ToManyRelationKey<E> = Exclude<RelationKey<E>, ToOneRelationKey<E>>;
|
|
144
144
|
/**
|
|
145
145
|
* sort by map - supports field keys, JSON dot-notation paths (restricted to real JSON fields,
|
|
146
|
-
* like `
|
|
146
|
+
* like `QueryWhere`), relation sort via nested objects, and vector similarity search on
|
|
147
147
|
* `number[]` fields. `Vector` is what confines a vector search to the level the statement ranks:
|
|
148
148
|
* the queried entity. A relation of it is joined in one row at a time, so there is nothing to rank
|
|
149
149
|
* there - the SQL dialects throw, and MongoDB would quietly drop it, so this is its only guard.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FieldKey, JsonFieldPaths, JsonFieldPathValue, RelationKey, RelationTarget } from './entity.js';
|
|
2
2
|
import type { QueryRaw } from './queryRaw.js';
|
|
3
3
|
import type { ExpandScalar, IsMany, QueryComparableScalar, Scalar } from './utility.js';
|
|
4
4
|
import type { QueryVectorQuery } from './vector.js';
|
|
@@ -20,12 +20,6 @@ export type QueryTextSearchOptions<E> = {
|
|
|
20
20
|
*/
|
|
21
21
|
$config?: string;
|
|
22
22
|
};
|
|
23
|
-
/**
|
|
24
|
-
* comparison by fields.
|
|
25
|
-
*/
|
|
26
|
-
export type QueryWhereFieldMap<E> = {
|
|
27
|
-
[K in FieldKey<E>]?: QueryWhereFieldValue<E[K]>;
|
|
28
|
-
};
|
|
29
23
|
/**
|
|
30
24
|
* Field comparison, JSON dot-path access, and relation filtering - all fully typed.
|
|
31
25
|
* JSON dot-paths are restricted to real JSON fields, and typed payloads type each path's value
|
|
@@ -37,9 +31,12 @@ export type QueryWhereFieldMap<E> = {
|
|
|
37
31
|
* {@link QuerySortMap} is: the sets are disjoint, and an assignability check against an
|
|
38
32
|
* intersection is repeated per constituent, which every `$where` in a codebase pays. The root
|
|
39
33
|
* operators stay a separate member - they are a fixed shape, not keyed off the entity.
|
|
34
|
+
*
|
|
35
|
+
* An object and nothing else: in a union with ids or lists, TypeScript reports a wrong value against
|
|
36
|
+
* the whole `$where` instead of the key holding it. Ids are `{ id: 1 }`, or the by-id methods.
|
|
40
37
|
*/
|
|
41
|
-
export type
|
|
42
|
-
[K in FieldKey<E> | RelationKey<E> | JsonFieldPaths<E>]?: K extends FieldKey<E> ? QueryWhereFieldValue<E[K]> : K extends RelationKey<E> ?
|
|
38
|
+
export type QueryWhere<E> = QueryWhereRootOperator<E> & {
|
|
39
|
+
[K in FieldKey<E> | RelationKey<E> | JsonFieldPaths<E>]?: K extends FieldKey<E> ? QueryWhereFieldValue<E[K]> : K extends RelationKey<E> ? QueryWhere<RelationTarget<E[K]>> | QueryRelationSizeFilter : QueryWhereFieldValue<JsonFieldPathValue<E, K & string>>;
|
|
43
40
|
};
|
|
44
41
|
/**
|
|
45
42
|
* Filter a to-many relation by its row count.
|
|
@@ -332,14 +329,5 @@ export type QueryWhereFieldValue<T> = T | (undefined extends T ? null : never) |
|
|
|
332
329
|
/**
|
|
333
330
|
* query filter array - the value every {@link QueryGroupOp} takes.
|
|
334
331
|
*/
|
|
335
|
-
export type QueryWhereArray<E> = (
|
|
336
|
-
/**
|
|
337
|
-
* query filter.
|
|
338
|
-
*/
|
|
339
|
-
/**
|
|
340
|
-
* `EntityId` rather than `IdValue`: a by-id method reduces to `$where: id`, and a composite key is
|
|
341
|
-
* addressed by an object carrying every key. That object is a where map naming those columns, so the
|
|
342
|
-
* two spellings meet here rather than needing a conversion.
|
|
343
|
-
*/
|
|
344
|
-
export type QueryWhere<E> = EntityId<E> | EntityId<E>[] | QueryWhereMap<E> | QueryWhereArray<E> | QueryRaw;
|
|
332
|
+
export type QueryWhereArray<E> = (QueryWhere<E> | QueryRaw)[];
|
|
345
333
|
export {};
|
package/dist/type/vector.d.ts
CHANGED
|
@@ -58,13 +58,15 @@ export type WithDistance<E, K extends string = '_distance'> = E & Record<K, numb
|
|
|
58
58
|
*
|
|
59
59
|
* `opsSuffix` rides along on the operator form because pgvector's index operator class is named from
|
|
60
60
|
* the same metric (`vector_cosine_ops`): keeping them together is what stops a dialect from having
|
|
61
|
-
* the operator but not the class it indexes with.
|
|
61
|
+
* the operator but not the class it indexes with. `metricArg` is for the engine with one function
|
|
62
|
+
* taking the metric by name: SQL Server's `VECTOR_DISTANCE('cosine', a, b)`.
|
|
62
63
|
*/
|
|
63
64
|
export type VectorMetric = {
|
|
64
65
|
readonly op: string;
|
|
65
66
|
readonly opsSuffix: string;
|
|
66
67
|
} | {
|
|
67
68
|
readonly fn: string;
|
|
69
|
+
readonly metricArg?: string;
|
|
68
70
|
};
|
|
69
71
|
/** The operator form, for the pgvector-family dialects whose index DDL also needs `opsSuffix`. */
|
|
70
72
|
export type VectorOperatorMetric = Extract<VectorMetric, {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type CascadeType, type EntityData, type EntityIndexMeta, type EntityMeta, type FieldKey, type FieldOptions, type JsonUpdateOp, type OnFieldCallback, type Query, type QueryAggMap, type QueryAggregateOp, type QueryExclude, type QueryGroupMap, type QueryOptions, QueryRaw, type QuerySearch, type QuerySelect, type QuerySelectValue, type QuerySizeComparisonOps, type QuerySortMap, type QueryVectorSearch, type QueryWhere, type
|
|
1
|
+
import { type CascadeType, type EntityData, type EntityId, type EntityIndexMeta, type EntityMeta, type FieldKey, type FieldOptions, type JsonUpdateOp, type OnFieldCallback, type Query, type QueryAggMap, type QueryAggregateOp, type QueryExclude, type QueryGroupMap, type QueryOptions, QueryRaw, type QuerySearch, type QuerySelect, type QuerySelectValue, type QuerySizeComparisonOps, type QuerySortMap, type QueryVectorSearch, type QueryWhere, type RelationKey } from '../type/index.js';
|
|
2
2
|
export type CallbackKey = keyof Pick<FieldOptions, 'onInsert' | 'onUpdate'>;
|
|
3
3
|
export declare function filterFieldKeys<E>(meta: EntityMeta<E>, payload: EntityData<E>, callbackKey: CallbackKey): FieldKey<E>[];
|
|
4
4
|
/** Appends `record`'s not-yet-`seen` insertable keys (real, caller-written, defined value) to `keys`. */
|
|
@@ -90,13 +90,16 @@ export declare function findVectorIndex<E>(meta: EntityMeta<E>, key: string): En
|
|
|
90
90
|
export declare function hasVectorNear(where: unknown): boolean;
|
|
91
91
|
/** Type guard: checks whether an update payload value is a JSON operator object. */
|
|
92
92
|
export declare function isJsonUpdateOp(value: unknown): value is JsonUpdateOp;
|
|
93
|
-
export declare function augmentWhere<E>(meta: EntityMeta<E>, target?: QueryWhere<E>, source?: QueryWhere<E>): QueryWhere<E>;
|
|
94
93
|
/**
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
* {@link applyFilters} and {@link augmentWhere} return new objects instead.
|
|
94
|
+
* The `$where` naming rows by key: a bare value names the one key column (refused on a composite), a
|
|
95
|
+
* composite's key map is a `$where` already, and a list is an `IN` of bare values or an OR of maps.
|
|
98
96
|
*/
|
|
99
|
-
export declare function
|
|
97
|
+
export declare function whereIds<E>(meta: EntityMeta<E>, ids: EntityId<E> | EntityId<E>[]): QueryWhere<E>;
|
|
98
|
+
/**
|
|
99
|
+
* Refuses a `$where` that is not a map. Untyped JS and parsed JSON can still pass an id or a list of
|
|
100
|
+
* them, and a scalar read as a map has no keys: the statement would address every row.
|
|
101
|
+
*/
|
|
102
|
+
export declare function assertWhere<E>(meta: EntityMeta<E>, where: unknown): void;
|
|
100
103
|
/** Returns a `QueryOptions.filters` value with the built-in soft-delete filter disabled (used by hard delete). */
|
|
101
104
|
export declare function withoutSoftDeleteFilter(filters: QueryOptions['filters']): QueryOptions['filters'];
|
|
102
105
|
/**
|
|
@@ -112,7 +115,7 @@ export declare function withoutSoftDeleteFilter(filters: QueryOptions['filters']
|
|
|
112
115
|
* (`{}`) resolved to "no restriction" and adds nothing - the escape hatch for trusted cross-tenant
|
|
113
116
|
* work (e.g. a maintenance job running under a `system` context).
|
|
114
117
|
*/
|
|
115
|
-
export declare function applyFilters<E>(meta: EntityMeta<E>, whereMap:
|
|
118
|
+
export declare function applyFilters<E>(meta: EntityMeta<E>, whereMap: QueryWhere<E>, opts?: QueryOptions): QueryWhere<E>;
|
|
116
119
|
/**
|
|
117
120
|
* Parsed entry from a `$group` map - either a raw group key or an aggregate function call.
|
|
118
121
|
*/
|
|
@@ -3,7 +3,7 @@ import { soleIdOf } from '../entity/metadata/definition.js';
|
|
|
3
3
|
import { QueryRaw, resolveAggregateOp, SOFT_DELETE_FILTER, } from '../type/index.js';
|
|
4
4
|
import { VECTOR_INDEX_TYPES } from '../type/vector.js';
|
|
5
5
|
import { isDatabaseWritten } from './field.util.js';
|
|
6
|
-
import { entityName, getFieldKeys, getKeys, hasKeys, isScalarId, someKey } from './object.util.js';
|
|
6
|
+
import { entityName, getFieldKeys, getKeys, hasKeys, isScalarId, isWhereMap, someKey } from './object.util.js';
|
|
7
7
|
export function filterFieldKeys(meta, payload, callbackKey) {
|
|
8
8
|
return getKeys(payload).filter((key) => {
|
|
9
9
|
const fieldOpts = meta.fields[key];
|
|
@@ -234,37 +234,24 @@ const JSON_UPDATE_OPS = [
|
|
|
234
234
|
export function isJsonUpdateOp(value) {
|
|
235
235
|
return value !== null && typeof value === 'object' && someKey(value, (key) => JSON_UPDATE_OPS.includes(key));
|
|
236
236
|
}
|
|
237
|
-
export function augmentWhere(meta, target = {}, source = {}) {
|
|
238
|
-
const targetComparison = buildQueryWhereAsMap(meta, target);
|
|
239
|
-
const sourceComparison = buildQueryWhereAsMap(meta, source);
|
|
240
|
-
return {
|
|
241
|
-
...targetComparison,
|
|
242
|
-
...sourceComparison,
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
237
|
/**
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
* {@link applyFilters} and {@link augmentWhere} return new objects instead.
|
|
238
|
+
* The `$where` naming rows by key: a bare value names the one key column (refused on a composite), a
|
|
239
|
+
* composite's key map is a `$where` already, and a list is an `IN` of bare values or an OR of maps.
|
|
249
240
|
*/
|
|
250
|
-
export function
|
|
251
|
-
if (
|
|
252
|
-
return {
|
|
253
|
-
}
|
|
254
|
-
if (Array.isArray(filter)) {
|
|
255
|
-
// A list of bare ids is an `IN` over the one key column; a list of anything else is a list of
|
|
256
|
-
// `$where`s, which is an OR - and that is how a composite's id objects name a settled set of rows.
|
|
257
|
-
return filter.every(isScalarId)
|
|
258
|
-
? { [soleIdOf(meta, 'addressing by a bare id value')]: filter }
|
|
259
|
-
: { $or: filter };
|
|
241
|
+
export function whereIds(meta, ids) {
|
|
242
|
+
if (Array.isArray(ids) ? ids.every(isScalarId) : isScalarId(ids)) {
|
|
243
|
+
return { [soleIdOf(meta, 'addressing by a bare id value')]: ids };
|
|
260
244
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
245
|
+
return (Array.isArray(ids) ? { $or: ids } : ids);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Refuses a `$where` that is not a map. Untyped JS and parsed JSON can still pass an id or a list of
|
|
249
|
+
* them, and a scalar read as a map has no keys: the statement would address every row.
|
|
250
|
+
*/
|
|
251
|
+
export function assertWhere(meta, where) {
|
|
252
|
+
if (!isWhereMap(where)) {
|
|
253
|
+
throw new TypeError(`$where on '${entityName(meta)}' must be a map of conditions, such as { id: 1 }`);
|
|
266
254
|
}
|
|
267
|
-
return filter;
|
|
268
255
|
}
|
|
269
256
|
/** Returns a `QueryOptions.filters` value with the built-in soft-delete filter disabled (used by hard delete). */
|
|
270
257
|
export function withoutSoftDeleteFilter(filters) {
|
|
@@ -314,7 +301,7 @@ export function applyFilters(meta, whereMap, opts) {
|
|
|
314
301
|
}
|
|
315
302
|
continue;
|
|
316
303
|
}
|
|
317
|
-
const conditionMap =
|
|
304
|
+
const conditionMap = condition;
|
|
318
305
|
if (!hasKeys(conditionMap)) {
|
|
319
306
|
continue; // resolved to "no restriction" (e.g. a trusted system context) - nothing to merge
|
|
320
307
|
}
|
|
@@ -35,3 +35,5 @@ export declare function getFieldKeys<E>(fields: {
|
|
|
35
35
|
* is what a `$where` map and a composite key's id object both are; an array is a list of either.
|
|
36
36
|
*/
|
|
37
37
|
export declare function isScalarId(value: unknown): boolean;
|
|
38
|
+
/** Whether `value` is a plain object naming columns, the one shape a `$where` takes. */
|
|
39
|
+
export declare function isWhereMap(value: unknown): value is Record<string, unknown>;
|
package/dist/util/object.util.js
CHANGED
|
@@ -81,3 +81,7 @@ export function isScalarId(value) {
|
|
|
81
81
|
const proto = Object.getPrototypeOf(value);
|
|
82
82
|
return proto !== Object.prototype && proto !== null;
|
|
83
83
|
}
|
|
84
|
+
/** Whether `value` is a plain object naming columns, the one shape a `$where` takes. */
|
|
85
|
+
export function isWhereMap(value) {
|
|
86
|
+
return !Array.isArray(value) && !isScalarId(value);
|
|
87
|
+
}
|