uql-orm 0.69.0 → 0.71.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 +2 -0
- package/dist/browser/querier/httpQuerier.d.ts +7 -7
- package/dist/browser/type/clientQuerier.d.ts +5 -5
- package/dist/browser/uql-browser.min.js +2 -2
- package/dist/browser/uql-browser.min.js.map +4 -4
- package/dist/cockroachdb/cockroachDialect.d.ts +6 -0
- package/dist/cockroachdb/cockroachDialect.js +10 -2
- package/dist/d1/d1SqliteDialect.d.ts +3 -0
- package/dist/d1/d1SqliteDialect.js +3 -1
- package/dist/dialect/abstractDialect.d.ts +8 -2
- package/dist/dialect/abstractDialect.js +17 -1
- package/dist/dialect/abstractSqlDialect.d.ts +21 -5
- package/dist/dialect/abstractSqlDialect.js +91 -46
- package/dist/dialect/aliases.d.ts +10 -7
- package/dist/dialect/aliases.js +10 -7
- package/dist/dialect/hydrateColumn.d.ts +3 -2
- package/dist/dialect/hydrateColumn.js +10 -1
- package/dist/dialect/mysqlLikeSqlDialect.js +5 -3
- package/dist/dialect/pgLikeSqlDialect.d.ts +17 -5
- package/dist/dialect/pgLikeSqlDialect.js +34 -15
- package/dist/dialect/queryJoins.d.ts +19 -1
- package/dist/dialect/queryJoins.js +54 -11
- package/dist/dialect/vectorCast.d.ts +2 -0
- package/dist/dialect/vectorCast.js +7 -0
- package/dist/dialect/vectorSqlDialect.d.ts +7 -3
- package/dist/dialect/vectorSqlDialect.js +15 -10
- package/dist/entity/decorator/members.d.ts +25 -11
- package/dist/entity/metadata/definition.d.ts +8 -3
- package/dist/libsql/libsqlDialect.d.ts +1 -1
- package/dist/libsql/libsqlDialect.js +3 -3
- package/dist/maria/mariaDialect.d.ts +3 -9
- package/dist/maria/mariaDialect.js +4 -13
- package/dist/maria/mariadbQuerier.js +9 -3
- package/dist/migrate/codegen/entityCodeGenerator.js +4 -2
- package/dist/migrate/ddl/index.d.ts +1 -0
- package/dist/migrate/ddl/index.js +4 -2
- package/dist/migrate/ddl/indexDdl.d.ts +2 -0
- package/dist/migrate/ddl/indexDdl.js +10 -2
- package/dist/migrate/ddl/pgIndexDdl.d.ts +3 -4
- package/dist/migrate/ddl/pgIndexDdl.js +11 -11
- package/dist/migrate/ddl/sqliteIndexDdl.d.ts +11 -0
- package/dist/migrate/ddl/sqliteIndexDdl.js +38 -0
- package/dist/migrate/generator/mongoCommand.d.ts +28 -0
- package/dist/migrate/generator/mongoCommand.js +8 -0
- package/dist/migrate/generator/mongoSchemaGenerator.d.ts +2 -0
- package/dist/migrate/generator/mongoSchemaGenerator.js +50 -6
- package/dist/migrate/introspection/mongoIntrospector.js +33 -7
- package/dist/migrate/introspection/postgresIntrospector.js +19 -0
- package/dist/migrate/introspection/sqliteIntrospector.d.ts +6 -0
- package/dist/migrate/introspection/sqliteIntrospector.js +29 -3
- package/dist/migrate/migrator.d.ts +2 -1
- package/dist/migrate/migrator.js +5 -3
- package/dist/mongo/mongoDialect.d.ts +33 -27
- package/dist/mongo/mongoDialect.js +178 -114
- package/dist/mongo/mongodbQuerier.d.ts +0 -2
- package/dist/mongo/mongodbQuerier.js +14 -13
- package/dist/mssql/mssqlDialect.js +4 -2
- package/dist/postgres/postgresDialect.js +2 -2
- package/dist/querier/abstractQuerier.d.ts +16 -11
- package/dist/querier/abstractQuerier.js +28 -8
- package/dist/querier/abstractQuerierPool.d.ts +9 -9
- package/dist/querier/abstractSqlQuerier.d.ts +1 -1
- package/dist/querier/abstractSqlQuerier.js +3 -3
- package/dist/schema/canonicalType.js +10 -4
- package/dist/schema/indexDifferences.d.ts +5 -2
- package/dist/schema/indexDifferences.js +5 -0
- package/dist/schema/schemaASTBuilder.js +3 -2
- package/dist/sqlite/localSqliteQuerierPool.d.ts +10 -11
- package/dist/sqlite/localSqliteQuerierPool.js +8 -11
- package/dist/sqlite/nodeSqliteQuerierPool.d.ts +4 -2
- package/dist/sqlite/nodeSqliteQuerierPool.js +7 -4
- package/dist/sqlite/sqliteDialect.d.ts +9 -1
- package/dist/sqlite/sqliteDialect.js +43 -3
- package/dist/sqlite/sqliteQuerierPool.d.ts +6 -5
- package/dist/sqlite/sqliteQuerierPool.js +10 -7
- package/dist/turso/tursoDialect.d.ts +1 -1
- package/dist/turso/tursoDialect.js +6 -2
- package/dist/turso/tursoLocalDialect.d.ts +1 -1
- package/dist/turso/tursoLocalDialect.js +1 -1
- package/dist/turso/tursoLocalQuerierPool.d.ts +4 -5
- package/dist/turso/tursoLocalQuerierPool.js +3 -10
- package/dist/type/dialect.d.ts +11 -0
- package/dist/type/entity.d.ts +45 -5
- package/dist/type/migration.d.ts +14 -9
- package/dist/type/query.d.ts +6 -0
- package/dist/type/queryAggregate.d.ts +73 -42
- package/dist/type/queryAggregate.js +4 -21
- package/dist/type/universalQuerier.d.ts +9 -9
- package/dist/type/vector.d.ts +17 -0
- package/dist/type/vector.js +6 -0
- package/dist/util/ddlExpression.util.d.ts +2 -0
- package/dist/util/ddlExpression.util.js +5 -0
- package/dist/util/dialect.util.d.ts +17 -3
- package/dist/util/dialect.util.js +40 -6
- package/package.json +5 -3
- package/skills/uql-orm/SKILL.md +142 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { assertSoleId, getMeta, idOf, namesKey, relationOf } from '../entity/index.js';
|
|
2
|
-
import { cascadesOnDelete, childrenOf, clone, entityName, fillOnFields, filterFieldKeys, filterPersistableRelationKeys, forEachRequestedRelation, getKeys, getRelationRequestSummary, idOnlyQuery, isPagedQuery, isScalarId, LoggerWrapper, parentJoins, queryLoggerFor, parseRelationAtKey, parseRelationQueryValue, rowKey, runHooks, someKey, targetKeyColumns, whereIds, withoutSoftDeleteFilter, } from '../util/index.js';
|
|
2
|
+
import { cascadesOnDelete, childrenOf, clone, entityName, fillOnFields, filterFieldKeys, filterPersistableRelationKeys, forEachRequestedRelation, getKeys, getRelationRequestSummary, idOnlyQuery, isPagedQuery, hasKeys, isScalarId, LoggerWrapper, parentJoins, queryLoggerFor, parseRelationAtKey, parseRelationQueryValue, rowKey, runHooks, someKey, targetKeyColumns, whereIds, withoutSoftDeleteFilter, } from '../util/index.js';
|
|
3
3
|
import { enrichError } from './queryError.js';
|
|
4
4
|
/**
|
|
5
5
|
* Refuses a nullish id, which would reduce to no filter at all, and a composite id missing a column,
|
|
@@ -27,6 +27,17 @@ function assertIdValue(entity, id) {
|
|
|
27
27
|
function soleParentColumn(relOpts) {
|
|
28
28
|
return parentJoins(relOpts, 1)[0].joined;
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* A bulk write names the rows it changes: a `$where`, or a `$limit` capping how many it reaches. The
|
|
32
|
+
* caller's own clauses are what count - a filter the entity adds, soft delete's, would otherwise make
|
|
33
|
+
* every table look narrowed, which is the case this exists to catch.
|
|
34
|
+
*/
|
|
35
|
+
function assertNamesRows(entity, method, q, opts) {
|
|
36
|
+
if (opts?.unfiltered || hasKeys(q?.$where) || q?.$limit !== undefined) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
throw new TypeError(`'${method}' over '${entity.name}' names no rows, so it would address every one: pass '{ unfiltered: true }' to mean it`);
|
|
40
|
+
}
|
|
30
41
|
/**
|
|
31
42
|
* The id each written row is named by, in payload order. Read off the rows as written, so a key the
|
|
32
43
|
* database generated or the ORM filled is there, and a composite is named by every column of it.
|
|
@@ -183,6 +194,7 @@ export class AbstractQuerier {
|
|
|
183
194
|
}
|
|
184
195
|
/** Settles the rows first where the update cascades, so a payload changing what `$where` reads still names them. */
|
|
185
196
|
async updateMany(entity, q, payload, opts) {
|
|
197
|
+
assertNamesRows(entity, 'updateMany', q, opts);
|
|
186
198
|
const meta = getMeta(entity);
|
|
187
199
|
return this.hooked(entity, 'Update', [payload], async ([row]) => {
|
|
188
200
|
fillOnFields(meta, [row], 'onUpdate');
|
|
@@ -206,9 +218,13 @@ export class AbstractQuerier {
|
|
|
206
218
|
const writes = filterFieldKeys(getMeta(entity), row, 'onUpdate').length > 0;
|
|
207
219
|
return writes ? this.internalUpdateMany(entity, q, row, opts) : unwritten;
|
|
208
220
|
}
|
|
209
|
-
/**
|
|
210
|
-
|
|
211
|
-
|
|
221
|
+
/**
|
|
222
|
+
* Whether a write has to name the rows `q` matches by their ids: no engine pages or orders an update or
|
|
223
|
+
* delete, and one without {@link DialectFeatures.correlatedWrites} cannot read a relation in its filter.
|
|
224
|
+
*/
|
|
225
|
+
settlesWrite(entity, q) {
|
|
226
|
+
const { dialect } = this;
|
|
227
|
+
return isPagedQuery(q) || (!dialect.features.correlatedWrites && dialect.constrainsRelations(entity, q.$where));
|
|
212
228
|
}
|
|
213
229
|
/** The ids `q` matches, in its own order and page. */
|
|
214
230
|
async settleIds(entity, q, opts) {
|
|
@@ -254,6 +270,7 @@ export class AbstractQuerier {
|
|
|
254
270
|
}
|
|
255
271
|
async deleteMany(entityOrQuery, qOrOpts, maybeOpts) {
|
|
256
272
|
const [entity, q, opts] = this.resolveEntityQuery(entityOrQuery, qOrOpts, maybeOpts);
|
|
273
|
+
assertNamesRows(entity, 'deleteMany', q, opts);
|
|
257
274
|
const meta = getMeta(entity);
|
|
258
275
|
const cascades = cascadesOnDelete(meta);
|
|
259
276
|
const watched = this.hasHook(entity, 'beforeDelete') || this.hasHook(entity, 'afterDelete');
|
|
@@ -350,8 +367,7 @@ export class AbstractQuerier {
|
|
|
350
367
|
const relOpts = relationOf(meta, relKey);
|
|
351
368
|
const relEntity = relOpts.entity();
|
|
352
369
|
const target = relOpts.through ? relOpts.through() : relEntity;
|
|
353
|
-
|
|
354
|
-
await this.deleteMany(target, { $where: where }, opts);
|
|
370
|
+
await this.deleteMany(target, { $where: childrenOf(parentJoins(relOpts, meta.ids.length), ids) }, opts);
|
|
355
371
|
}
|
|
356
372
|
}
|
|
357
373
|
/**
|
|
@@ -480,8 +496,12 @@ export class AbstractQuerier {
|
|
|
480
496
|
* the write filled in - a generated key, an `onInsert` value - without it landing on the caller's.
|
|
481
497
|
*/
|
|
482
498
|
async hooked(entity, event, payloads, write) {
|
|
483
|
-
|
|
484
|
-
|
|
499
|
+
// The one place a caller's write becomes the row the rest of the library handles. They are the
|
|
500
|
+
// same object: a write is the entity's data without the keys the database fills, which
|
|
501
|
+
// TypeScript cannot relate across an entity it has not resolved.
|
|
502
|
+
const asRows = payloads;
|
|
503
|
+
await this.emitHook(entity, `before${event}`, asRows);
|
|
504
|
+
const rows = clone(asRows);
|
|
485
505
|
const result = await write(rows);
|
|
486
506
|
await this.emitHook(entity, `after${event}`, rows);
|
|
487
507
|
return result;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AbstractDialect } from '../dialect/index.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { EntityWrite, EntityId, ExtraOptions, FieldKey, PoolRunOptions, Querier, QuerierPool, QueryAggMap, QueryAggregate, QueryAggregateResult, QueryConflictPaths, QueryFilter, QueryFindResult, QueryGroupMap, QueryOneProjected, QueryOptions, QueryPage, QueryProjected, QuerySearch, QueryUpsertOneResult, QueryUpsertManyResult, RelationKey, TransactionOptions, Type, UpdateWrite, WrittenId } from '../type/index.js';
|
|
3
3
|
/**
|
|
4
4
|
* Base pool: dialect id and behavior come only from the `dialect` instance (see {@link QuerierPool}).
|
|
5
5
|
*/
|
|
@@ -37,14 +37,14 @@ export declare abstract class AbstractQuerierPool<Q extends Querier, D extends A
|
|
|
37
37
|
exists<E extends object>(entity: Type<E>, q?: QueryFilter<E>, opts?: QueryOptions): Promise<boolean>;
|
|
38
38
|
aggregate<E extends object, const G extends QueryGroupMap<E>, const A extends QueryAggMap<E>>(entity: Type<E>, q: QueryAggregate<E, G, A>, opts?: QueryOptions): Promise<QueryAggregateResult<E, G, A>[]>;
|
|
39
39
|
estimatedCount<E extends object>(entity: Type<E>): Promise<number>;
|
|
40
|
-
insertOne<E extends object>(entity: Type<E>, payload:
|
|
41
|
-
insertMany<E extends object>(entity: Type<E>, payload:
|
|
42
|
-
updateOneById<E extends object>(entity: Type<E>, id: EntityId<E>, payload:
|
|
43
|
-
updateMany<E extends object>(entity: Type<E>, q: QuerySearch<E>, payload:
|
|
44
|
-
upsertOne<E extends object>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload:
|
|
45
|
-
upsertMany<E extends object>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload:
|
|
46
|
-
saveOne<E extends object>(entity: Type<E>, payload:
|
|
47
|
-
saveMany<E extends object>(entity: Type<E>, payload:
|
|
40
|
+
insertOne<E extends object>(entity: Type<E>, payload: EntityWrite<E>): Promise<WrittenId<E> | undefined>;
|
|
41
|
+
insertMany<E extends object>(entity: Type<E>, payload: EntityWrite<E>[]): Promise<(WrittenId<E> | undefined)[]>;
|
|
42
|
+
updateOneById<E extends object>(entity: Type<E>, id: EntityId<E>, payload: UpdateWrite<E>, opts?: QueryOptions): Promise<number>;
|
|
43
|
+
updateMany<E extends object>(entity: Type<E>, q: QuerySearch<E>, payload: UpdateWrite<E>, opts?: QueryOptions): Promise<number>;
|
|
44
|
+
upsertOne<E extends object>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: EntityWrite<E>): Promise<QueryUpsertOneResult<E>>;
|
|
45
|
+
upsertMany<E extends object>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: EntityWrite<E>[]): Promise<QueryUpsertManyResult<E>>;
|
|
46
|
+
saveOne<E extends object>(entity: Type<E>, payload: EntityWrite<E>): Promise<WrittenId<E> | undefined>;
|
|
47
|
+
saveMany<E extends object>(entity: Type<E>, payload: EntityWrite<E>[]): Promise<(WrittenId<E> | undefined)[]>;
|
|
48
48
|
deleteOneById<E extends object>(entity: Type<E>, id: EntityId<E>, opts?: QueryOptions): Promise<number>;
|
|
49
49
|
deleteMany<E extends object>(entity: Type<E>, q: QuerySearch<E>, opts?: QueryOptions): Promise<number>;
|
|
50
50
|
restoreOneById<E extends object>(entity: Type<E>, id: EntityId<E>): Promise<number>;
|
|
@@ -80,7 +80,7 @@ export declare abstract class AbstractSqlQuerier extends AbstractQuerier impleme
|
|
|
80
80
|
*/
|
|
81
81
|
private hydrateFields;
|
|
82
82
|
/**
|
|
83
|
-
* Runs a statement whose one row carries a {@link
|
|
83
|
+
* Runs a statement whose one row carries a {@link AGGREGATE_VALUE_ALIAS} column. `Number` because `COUNT(*)` is BIGINT and
|
|
84
84
|
* a caller supplying their own `types` replaces the decoding the pools do at the wire; `?? 0` because
|
|
85
85
|
* a catalog that does not know the table answers with no row, which is nothing counted.
|
|
86
86
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AGGREGATE_VALUE_ALIAS, TOTAL_ALIAS } from '../dialect/aliases.js';
|
|
2
2
|
import { decodeColumn } from '../dialect/hydrateColumn.js';
|
|
3
3
|
import { getMeta, namesKey } from '../entity/index.js';
|
|
4
4
|
import { COUNT_RESULT_KEY } from '../type/index.js';
|
|
@@ -290,13 +290,13 @@ export class AbstractSqlQuerier extends AbstractQuerier {
|
|
|
290
290
|
}
|
|
291
291
|
}
|
|
292
292
|
/**
|
|
293
|
-
* Runs a statement whose one row carries a {@link
|
|
293
|
+
* Runs a statement whose one row carries a {@link AGGREGATE_VALUE_ALIAS} column. `Number` because `COUNT(*)` is BIGINT and
|
|
294
294
|
* a caller supplying their own `types` replaces the decoding the pools do at the wire; `?? 0` because
|
|
295
295
|
* a catalog that does not know the table answers with no row, which is nothing counted.
|
|
296
296
|
*/
|
|
297
297
|
async runCount(build) {
|
|
298
298
|
const [row] = await this.query(build);
|
|
299
|
-
return Number(row?.[
|
|
299
|
+
return Number(row?.[AGGREGATE_VALUE_ALIAS] ?? 0);
|
|
300
300
|
}
|
|
301
301
|
async internalCount(entity, q, opts) {
|
|
302
302
|
return this.runCount((ctx) => this.dialect.count(ctx, entity, q, opts));
|
|
@@ -80,6 +80,7 @@ const SQL_TO_CANONICAL = {
|
|
|
80
80
|
image: { category: 'blob', size: 'big' },
|
|
81
81
|
// === Vector (for AI/embeddings) ===
|
|
82
82
|
vector: { category: 'vector' },
|
|
83
|
+
f32_blob: { category: 'vector' },
|
|
83
84
|
halfvec: { category: 'halfvec' },
|
|
84
85
|
sparsevec: { category: 'sparsevec' },
|
|
85
86
|
};
|
|
@@ -204,8 +205,8 @@ const ENGINE_TYPES = {
|
|
|
204
205
|
sizes: MYSQL_SIZES,
|
|
205
206
|
decimal: { precision: 10, scale: 2 },
|
|
206
207
|
},
|
|
207
|
-
// SQLite uses affinity, so no size variants.
|
|
208
|
-
sqlite: { scalars: withVectorType(SQLITE_SCALAR_MAP, '
|
|
208
|
+
// SQLite uses affinity, so no size variants. `F32_BLOB` is libSQL's vector type; elsewhere just a name of BLOB affinity.
|
|
209
|
+
sqlite: { scalars: withVectorType(SQLITE_SCALAR_MAP, 'F32_BLOB') },
|
|
209
210
|
// 2025 and up; below that the server refuses the type rather than storing it as text.
|
|
210
211
|
mssql: { scalars: withVectorType(MSSQL_SCALAR_MAP, 'VECTOR'), sizes: MSSQL_SIZES },
|
|
211
212
|
mongodb: { scalars: withVectorType(MONGO_SCALAR_MAP, 'array') },
|
|
@@ -238,7 +239,7 @@ export function sqlToCanonical(sqlType) {
|
|
|
238
239
|
const unsigned = normalized.includes('unsigned');
|
|
239
240
|
const withoutUnsigned = normalized.replace(/\s*unsigned\s*/i, ' ').trim();
|
|
240
241
|
// Extract base type and parameters: "VARCHAR(255)" -> ["varchar", "255"]
|
|
241
|
-
const match = withoutUnsigned.match(/^([a-z][a-z0-
|
|
242
|
+
const match = withoutUnsigned.match(/^([a-z][a-z0-9_ ]*?)(?:\(([^)]+)\))?$/);
|
|
242
243
|
const base = match ? SQL_TO_CANONICAL[match[1]] : undefined;
|
|
243
244
|
if (!match || !base) {
|
|
244
245
|
return { category: 'string', raw: sqlType };
|
|
@@ -332,8 +333,13 @@ export function canonicalToTypeScript(type) {
|
|
|
332
333
|
* the engine settles an unstated bound. Migrations and drift both compare through it.
|
|
333
334
|
*/
|
|
334
335
|
export function engineType(dialect) {
|
|
335
|
-
return (type) =>
|
|
336
|
+
return (type) => {
|
|
337
|
+
const stored = sqlToCanonical(canonicalToSql(type, dialect));
|
|
338
|
+
// SQLite keeps a vector in any column, so one created as `TEXT` before vectors were blobs stays as it is.
|
|
339
|
+
return dialect.dialectName === 'sqlite' && isVectorCategory(stored.category) ? SQLITE_TEXT : stored;
|
|
340
|
+
};
|
|
336
341
|
}
|
|
342
|
+
const SQLITE_TEXT = { category: 'string', size: 'small' };
|
|
337
343
|
/**
|
|
338
344
|
* Convert UQL FieldOptions to a canonical type.
|
|
339
345
|
*/
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { IndexNode } from './types.js';
|
|
2
|
-
/**
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* What an introspector reports about an index, and so all a diff may compare; apart from `IndexFeature`, what an engine emits.
|
|
4
|
+
* `vector` is whether it is a vector index at all, for an engine with one vector index whatever type declared it.
|
|
5
|
+
*/
|
|
6
|
+
export type IndexFacet = 'order' | 'nulls' | 'opsClass' | 'accessMethod' | 'include' | 'vector';
|
|
4
7
|
/**
|
|
5
8
|
* Whether the table has this index already, by shape rather than name, uniqueness included. An index
|
|
6
9
|
* over an expression, whose text the engine reprints, falls back to its name.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isVectorIndexType } from '../type/vector.js';
|
|
1
2
|
/**
|
|
2
3
|
* Whether the table has this index already, by shape rather than name, uniqueness included. An index
|
|
3
4
|
* over an expression, whose text the engine reprints, falls back to its name.
|
|
@@ -45,6 +46,10 @@ export function describeIndexDifferences(source, target, facets) {
|
|
|
45
46
|
if (facets.has('accessMethod') && (source.type ?? 'btree') !== (target.type ?? 'btree')) {
|
|
46
47
|
differences.push(`type: ${target.type ?? 'btree'} -> ${source.type ?? 'btree'}`);
|
|
47
48
|
}
|
|
49
|
+
if (facets.has('vector') && isVectorIndexType(source.type) !== isVectorIndexType(target.type)) {
|
|
50
|
+
const [expected, actual] = [source, target].map((index) => (isVectorIndexType(index.type) ? 'yes' : 'no'));
|
|
51
|
+
differences.push(`vector index: ${actual} -> ${expected}`);
|
|
52
|
+
}
|
|
48
53
|
if (facets.has('include')) {
|
|
49
54
|
// Order carries no meaning in an `INCLUDE` list, so it is compared as a set.
|
|
50
55
|
const [sourceInclude, targetInclude] = [source.include ?? [], target.include ?? []].map((columns) => [...columns].sort().join(', '));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { fieldOf, foreignKeysOf, getMeta, soleIdOf } from '../entity/metadata/definition.js';
|
|
2
|
-
import { declaredIndexes,
|
|
2
|
+
import { declaredIndexes, declaredIndexName, renderIndexColumn } from '../util/ddlExpression.util.js';
|
|
3
3
|
import { isInlinedExpression } from '../util/field.util.js';
|
|
4
4
|
import { isSoleIdField } from '../util/field.util.js';
|
|
5
5
|
import { isAutoIncrement } from '../util/field.util.js';
|
|
@@ -198,7 +198,7 @@ function addCompositeIndex(ctx, table, meta, idxMeta) {
|
|
|
198
198
|
});
|
|
199
199
|
if (!resolved.length)
|
|
200
200
|
return;
|
|
201
|
-
const name = idxMeta.name
|
|
201
|
+
const name = declaredIndexName(idxMeta.name, table.name, resolved);
|
|
202
202
|
ctx.ast.addIndex({
|
|
203
203
|
name,
|
|
204
204
|
table,
|
|
@@ -211,5 +211,6 @@ function addCompositeIndex(ctx, table, meta, idxMeta) {
|
|
|
211
211
|
m: idxMeta.m,
|
|
212
212
|
efConstruction: idxMeta.efConstruction,
|
|
213
213
|
lists: idxMeta.lists,
|
|
214
|
+
config: idxMeta.config,
|
|
214
215
|
});
|
|
215
216
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { AbstractSharedHandleQuerierPool } from '../querier/abstractSharedHandleQuerierPool.js';
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
4
|
-
import { type SqlitePreparedStatement, SqliteQuerier } from './sqliteQuerier.js';
|
|
2
|
+
import type { SqliteDialect } from './sqliteDialect.js';
|
|
3
|
+
import { type SqliteDatabase, type SqlitePreparedStatement, SqliteQuerier } from './sqliteQuerier.js';
|
|
5
4
|
/** What every local SQLite pool accepts on top of its driver's own options. */
|
|
6
5
|
export type LocalSqlitePoolOptions = {
|
|
7
6
|
/**
|
|
@@ -27,12 +26,12 @@ export type LocalSqliteDatabase = {
|
|
|
27
26
|
export declare function adaptSqlite<S extends Omit<SqlitePreparedStatement, 'reader'>>(db: Omit<LocalSqliteDatabase, 'prepare'> & {
|
|
28
27
|
prepare(sql: string): S;
|
|
29
28
|
}, reads: (stmt: S) => boolean): LocalSqliteDatabase;
|
|
30
|
-
/**
|
|
31
|
-
export declare
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
/** Opens the driver's database,
|
|
35
|
-
protected abstract createDb(): Promise<
|
|
36
|
-
protected openDb(): Promise<
|
|
37
|
-
protected buildQuerier(db:
|
|
29
|
+
/** `db` with each loadable extension installed, which `node:sqlite` refuses unless opened to allow them. */
|
|
30
|
+
export declare function loadExtensions(db: LocalSqliteDatabase, extensions?: readonly string[]): LocalSqliteDatabase;
|
|
31
|
+
/** A pool for a database file opened in this process, configured the same way whichever driver's {@link createDb} opens it. */
|
|
32
|
+
export declare abstract class AbstractLocalSqliteQuerierPool<DB extends SqliteDatabase, D extends SqliteDialect> extends AbstractSharedHandleQuerierPool<DB, SqliteQuerier, D> {
|
|
33
|
+
/** Opens the driver's database, reading integers as `bigint`, which the querier decodes exactly past 2^53. */
|
|
34
|
+
protected abstract createDb(): Promise<DB>;
|
|
35
|
+
protected openDb(): Promise<DB>;
|
|
36
|
+
protected buildQuerier(db: DB): SqliteQuerier;
|
|
38
37
|
}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { dialectOptionsFrom } from '../dialect/abstractDialect.js';
|
|
2
1
|
import { AbstractSharedHandleQuerierPool } from '../querier/abstractSharedHandleQuerierPool.js';
|
|
3
|
-
import { SqliteDialect } from './sqliteDialect.js';
|
|
4
2
|
import { applySqlitePragmas } from './sqlitePragmas.js';
|
|
5
3
|
import { SqliteQuerier } from './sqliteQuerier.js';
|
|
6
4
|
/**
|
|
@@ -22,19 +20,18 @@ export function adaptSqlite(db, reads) {
|
|
|
22
20
|
close: () => db.close(),
|
|
23
21
|
};
|
|
24
22
|
}
|
|
25
|
-
/**
|
|
26
|
-
export
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
super(new SqliteDialect(dialectOptionsFrom(extra)), extra);
|
|
30
|
-
this.opts = opts;
|
|
23
|
+
/** `db` with each loadable extension installed, which `node:sqlite` refuses unless opened to allow them. */
|
|
24
|
+
export function loadExtensions(db, extensions = []) {
|
|
25
|
+
for (const extension of extensions) {
|
|
26
|
+
db.loadExtension(extension);
|
|
31
27
|
}
|
|
28
|
+
return db;
|
|
29
|
+
}
|
|
30
|
+
/** A pool for a database file opened in this process, configured the same way whichever driver's {@link createDb} opens it. */
|
|
31
|
+
export class AbstractLocalSqliteQuerierPool extends AbstractSharedHandleQuerierPool {
|
|
32
32
|
async openDb() {
|
|
33
33
|
const db = await this.createDb();
|
|
34
34
|
await applySqlitePragmas(db);
|
|
35
|
-
for (const extension of this.opts?.extensions ?? []) {
|
|
36
|
-
db.loadExtension(extension);
|
|
37
|
-
}
|
|
38
35
|
return db;
|
|
39
36
|
}
|
|
40
37
|
buildQuerier(db) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ExtraOptions } from '../type/index.js';
|
|
2
2
|
import { AbstractLocalSqliteQuerierPool, type LocalSqliteDatabase, type LocalSqlitePoolOptions } from './localSqliteQuerierPool.js';
|
|
3
|
+
import { SqliteDialect } from './sqliteDialect.js';
|
|
3
4
|
/**
|
|
4
5
|
* The `DatabaseSync` options worth surfacing, plus the loadable extensions to install. Declared here
|
|
5
6
|
* rather than imported from `node:sqlite` so this module needs no ambient Node types; unknown keys
|
|
@@ -15,8 +16,9 @@ export type NodeSqlitePoolOptions = LocalSqlitePoolOptions & {
|
|
|
15
16
|
* A pool over Node's built-in `node:sqlite`, needing no dependency at all. {@link Sqlite3QuerierPool} is the
|
|
16
17
|
* faster choice for read-heavy work, and the one on Bun.
|
|
17
18
|
*/
|
|
18
|
-
export declare class NodeSqliteQuerierPool extends AbstractLocalSqliteQuerierPool<
|
|
19
|
+
export declare class NodeSqliteQuerierPool extends AbstractLocalSqliteQuerierPool<LocalSqliteDatabase, SqliteDialect> {
|
|
19
20
|
readonly filename: string;
|
|
20
|
-
|
|
21
|
+
readonly opts?: NodeSqlitePoolOptions | undefined;
|
|
22
|
+
constructor(filename?: string, opts?: NodeSqlitePoolOptions | undefined, extra?: ExtraOptions);
|
|
21
23
|
protected createDb(): Promise<LocalSqliteDatabase>;
|
|
22
24
|
}
|
|
@@ -1,24 +1,27 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { dialectOptionsFrom } from '../dialect/abstractDialect.js';
|
|
2
|
+
import { AbstractLocalSqliteQuerierPool, adaptSqlite, loadExtensions, } from './localSqliteQuerierPool.js';
|
|
3
|
+
import { SqliteDialect } from './sqliteDialect.js';
|
|
2
4
|
/**
|
|
3
5
|
* A pool over Node's built-in `node:sqlite`, needing no dependency at all. {@link Sqlite3QuerierPool} is the
|
|
4
6
|
* faster choice for read-heavy work, and the one on Bun.
|
|
5
7
|
*/
|
|
6
8
|
export class NodeSqliteQuerierPool extends AbstractLocalSqliteQuerierPool {
|
|
7
9
|
filename;
|
|
10
|
+
opts;
|
|
8
11
|
constructor(filename = ':memory:', opts, extra) {
|
|
9
|
-
super(
|
|
12
|
+
super(new SqliteDialect(dialectOptionsFrom(extra)), extra);
|
|
10
13
|
this.filename = filename;
|
|
14
|
+
this.opts = opts;
|
|
11
15
|
}
|
|
12
16
|
async createDb() {
|
|
13
17
|
const { DatabaseSync } = await import('node:sqlite');
|
|
14
18
|
const { extensions, ...driverOpts } = this.opts ?? {};
|
|
15
19
|
const nodeDb = new DatabaseSync(this.filename, {
|
|
16
20
|
...driverOpts,
|
|
17
|
-
// Integers as `bigint`, which the querier decodes exactly past 2^53.
|
|
18
21
|
readBigInts: true,
|
|
19
22
|
// `node:sqlite` refuses `loadExtension` unless the database was opened with this on.
|
|
20
23
|
...(extensions?.length ? { allowExtension: true } : undefined),
|
|
21
24
|
});
|
|
22
|
-
return adaptSqlite(nodeDb, (stmt) => stmt.columns().length > 0);
|
|
25
|
+
return loadExtensions(adaptSqlite(nodeDb, (stmt) => stmt.columns().length > 0), extensions);
|
|
23
26
|
}
|
|
24
27
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AbstractSqlDialect, type DerivedRelation, type HydrateKind, type RelationRows } from '../dialect/abstractSqlDialect.js';
|
|
2
2
|
import { type JsonAccessMode, type JsonSlot } from '../dialect/jsonSql.js';
|
|
3
|
-
import type
|
|
3
|
+
import { type EntityMeta, type FieldOptions, type Query, type QueryContext, type QueryPager, type QueryTextSearchOptions, type QueryWhere, type SqlDialectFeatures, type Type, type VectorDistance, type VectorMetric } from '../type/index.js';
|
|
4
4
|
/** What SQLite and the engines derived from it have. */
|
|
5
5
|
export declare const SQLITE_FEATURES: SqlDialectFeatures;
|
|
6
6
|
export declare class SqliteDialect extends AbstractSqlDialect {
|
|
@@ -24,6 +24,12 @@ export declare class SqliteDialect extends AbstractSqlDialect {
|
|
|
24
24
|
* their own vector functions instead, so `LibsqlDialect` overrides this.
|
|
25
25
|
*/
|
|
26
26
|
readonly vectorMetrics: ReadonlyMap<VectorDistance, VectorMetric>;
|
|
27
|
+
/**
|
|
28
|
+
* A read ranked by the metric its field's vector index measures, and paged, narrowed to the rowids of
|
|
29
|
+
* that index's nearest rows, which libSQL's `vector_top_k` answers: `$candidates` of them, else as many
|
|
30
|
+
* as the page reaches. Their exact distance still orders them. Unchanged on an engine with no such index.
|
|
31
|
+
*/
|
|
32
|
+
protected rankedWhere<E>(meta: EntityMeta<E>, q: Query<E>, prefix: string | undefined): QueryWhere<E> | undefined;
|
|
27
33
|
/**
|
|
28
34
|
* SQLite does not support the `DEFAULT` keyword inside `VALUES`. Inline the metadata default
|
|
29
35
|
* when declared, else `NULL` (which is also how SQLite auto-generates INTEGER PRIMARY KEYs).
|
|
@@ -51,7 +57,9 @@ export declare class SqliteDialect extends AbstractSqlDialect {
|
|
|
51
57
|
protected readonly carriedFields: {
|
|
52
58
|
numeric: (expr: string, field: FieldOptions) => string;
|
|
53
59
|
blob: (expr: string) => string;
|
|
60
|
+
vector: (expr: string) => string;
|
|
54
61
|
};
|
|
62
|
+
private bytesAsText;
|
|
55
63
|
/** A date reads back as SQLite stored it, a number or text, which JSON carries unchanged. */
|
|
56
64
|
protected hydrateKind(field: FieldOptions | undefined): HydrateKind | undefined;
|
|
57
65
|
/**
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { AbstractSqlDialect, } from '../dialect/abstractSqlDialect.js';
|
|
2
2
|
import { BYTES_PREFIX } from '../dialect/hydrateColumn.js';
|
|
3
3
|
import { chainedCall, groupsPerCall, jsonSetCall, jsonPath, jsonArraySlotArgs, jsonSlotArgs, jsonRemoveCall, jsonSetTarget, } from '../dialect/jsonSql.js';
|
|
4
|
-
import {
|
|
4
|
+
import { QueryRaw, } from '../type/index.js';
|
|
5
|
+
import { indexDistance, isVectorIndexType } from '../type/vector.js';
|
|
6
|
+
import { declaredIndexName } from '../util/ddlExpression.util.js';
|
|
7
|
+
import { findVectorIndex, findVectorSort, textSearchFields, vectorCandidates } from '../util/dialect.util.js';
|
|
5
8
|
import { columnFamily, isIntegerColumn } from '../util/field.util.js';
|
|
6
9
|
/** What SQLite and the engines derived from it have. */
|
|
7
10
|
export const SQLITE_FEATURES = {
|
|
@@ -14,11 +17,13 @@ export const SQLITE_FEATURES = {
|
|
|
14
17
|
generatedColumnAdd: false, // accepted in a CREATE TABLE, rejected in an ALTER
|
|
15
18
|
commentSyntax: 'none',
|
|
16
19
|
vectorIndexRequiresNotNull: false,
|
|
17
|
-
vectorSupportsLength:
|
|
20
|
+
vectorSupportsLength: true,
|
|
21
|
+
vectorBytes: true,
|
|
18
22
|
supportsTimestamptz: false,
|
|
19
23
|
stringSizing: 'text',
|
|
20
24
|
supportsUnsigned: false,
|
|
21
25
|
serverSideCursors: false,
|
|
26
|
+
correlatedWrites: true,
|
|
22
27
|
rowLocks: false,
|
|
23
28
|
rowLockWithWindow: true,
|
|
24
29
|
rowLockOf: true,
|
|
@@ -54,6 +59,37 @@ export class SqliteDialect extends AbstractSqlDialect {
|
|
|
54
59
|
['l2', { fn: 'vec_distance_L2' }],
|
|
55
60
|
['l1', { fn: 'vec_distance_L1' }],
|
|
56
61
|
]);
|
|
62
|
+
/**
|
|
63
|
+
* A read ranked by the metric its field's vector index measures, and paged, narrowed to the rowids of
|
|
64
|
+
* that index's nearest rows, which libSQL's `vector_top_k` answers: `$candidates` of them, else as many
|
|
65
|
+
* as the page reaches. Their exact distance still orders them. Unchanged on an engine with no such index.
|
|
66
|
+
*/
|
|
67
|
+
rankedWhere(meta, q, prefix) {
|
|
68
|
+
const ranked = findVectorSort(q.$sort);
|
|
69
|
+
const k = vectorCandidates(q) ?? (q.$limit === undefined ? undefined : (q.$skip ?? 0) + q.$limit);
|
|
70
|
+
const index = ranked && findVectorIndex(meta, ranked.key);
|
|
71
|
+
if (!ranked || !index || !isVectorIndexType(index.type) || k === undefined) {
|
|
72
|
+
return q.$where;
|
|
73
|
+
}
|
|
74
|
+
const { colName, distance, field } = this.resolveVectorDistance(meta, ranked.key, ranked.search);
|
|
75
|
+
if (indexDistance(index) !== distance || !this.vectorMetrics.get(distance)?.index) {
|
|
76
|
+
return q.$where;
|
|
77
|
+
}
|
|
78
|
+
const name = declaredIndexName(index.name, this.resolveTableName(meta), [{ column: colName }]);
|
|
79
|
+
const table = this.escapeId(prefix ?? this.resolveTableAlias(meta), true, true);
|
|
80
|
+
const nearest = new QueryRaw(({ ctx }) => {
|
|
81
|
+
ctx.append(`${table}rowid IN (SELECT id FROM vector_top_k(`);
|
|
82
|
+
ctx.addValue(name);
|
|
83
|
+
ctx.append(', ');
|
|
84
|
+
this.appendVectorValue(ctx, ranked.search.$vector, field);
|
|
85
|
+
ctx.append(', ');
|
|
86
|
+
ctx.addValue(k);
|
|
87
|
+
ctx.append('))');
|
|
88
|
+
});
|
|
89
|
+
const where = {};
|
|
90
|
+
where.$and = q.$where ? [q.$where, nearest] : [nearest];
|
|
91
|
+
return where;
|
|
92
|
+
}
|
|
57
93
|
/**
|
|
58
94
|
* SQLite does not support the `DEFAULT` keyword inside `VALUES`. Inline the metadata default
|
|
59
95
|
* when declared, else `NULL` (which is also how SQLite auto-generates INTEGER PRIMARY KEYs).
|
|
@@ -108,8 +144,12 @@ export class SqliteDialect extends AbstractSqlDialect {
|
|
|
108
144
|
*/
|
|
109
145
|
carriedFields = {
|
|
110
146
|
numeric: (expr, field) => (isIntegerColumn(field) ? `CAST(${expr} AS TEXT)` : expr),
|
|
111
|
-
blob: (expr) =>
|
|
147
|
+
blob: (expr) => this.bytesAsText(expr),
|
|
148
|
+
vector: (expr) => `CASE WHEN typeof(${expr}) = 'blob' THEN ${this.bytesAsText(expr)} ELSE ${expr} END`,
|
|
112
149
|
};
|
|
150
|
+
bytesAsText(expr) {
|
|
151
|
+
return `${this.escape(BYTES_PREFIX)} || hex(${expr})`;
|
|
152
|
+
}
|
|
113
153
|
/** A date reads back as SQLite stored it, a number or text, which JSON carries unchanged. */
|
|
114
154
|
hydrateKind(field) {
|
|
115
155
|
return columnFamily(field?.type) === 'date' ? undefined : super.hydrateKind(field);
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import type { Options } from 'better-sqlite3';
|
|
2
2
|
import type { ExtraOptions } from '../type/index.js';
|
|
3
3
|
import { AbstractLocalSqliteQuerierPool, type LocalSqliteDatabase, type LocalSqlitePoolOptions } from './localSqliteQuerierPool.js';
|
|
4
|
+
import { SqliteDialect } from './sqliteDialect.js';
|
|
4
5
|
/** Driver options, plus the loadable extensions to install on the connection. */
|
|
5
6
|
export type Sqlite3PoolOptions = Options & LocalSqlitePoolOptions;
|
|
6
7
|
/**
|
|
7
8
|
* Pool for `better-sqlite3`, or `bun:sqlite` when running under Bun - the same file, through whichever
|
|
8
9
|
* driver the runtime provides.
|
|
9
10
|
*/
|
|
10
|
-
export declare class Sqlite3QuerierPool extends AbstractLocalSqliteQuerierPool<
|
|
11
|
+
export declare class Sqlite3QuerierPool extends AbstractLocalSqliteQuerierPool<LocalSqliteDatabase, SqliteDialect> {
|
|
11
12
|
readonly filename: string | Buffer;
|
|
12
|
-
|
|
13
|
+
readonly opts?: Sqlite3PoolOptions | undefined;
|
|
14
|
+
constructor(filename?: string | Buffer, opts?: Sqlite3PoolOptions | undefined, extra?: ExtraOptions);
|
|
13
15
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* serialized database - which better-sqlite3 takes as its filename - is deserialized there instead.
|
|
16
|
+
* `bun:sqlite` rejects option keys it does not know, so `extensions` is stripped out, and opens only a
|
|
17
|
+
* path, so a serialized database - which better-sqlite3 takes as its filename - is deserialized there instead.
|
|
17
18
|
*/
|
|
18
19
|
protected createDb(): Promise<LocalSqliteDatabase>;
|
|
19
20
|
}
|
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { dialectOptionsFrom } from '../dialect/abstractDialect.js';
|
|
2
|
+
import { AbstractLocalSqliteQuerierPool, adaptSqlite, loadExtensions, } from './localSqliteQuerierPool.js';
|
|
3
|
+
import { SqliteDialect } from './sqliteDialect.js';
|
|
2
4
|
/**
|
|
3
5
|
* Pool for `better-sqlite3`, or `bun:sqlite` when running under Bun - the same file, through whichever
|
|
4
6
|
* driver the runtime provides.
|
|
5
7
|
*/
|
|
6
8
|
export class Sqlite3QuerierPool extends AbstractLocalSqliteQuerierPool {
|
|
7
9
|
filename;
|
|
10
|
+
opts;
|
|
8
11
|
constructor(filename = ':memory:', opts, extra) {
|
|
9
|
-
super(
|
|
12
|
+
super(new SqliteDialect(dialectOptionsFrom(extra)), extra);
|
|
10
13
|
this.filename = filename;
|
|
14
|
+
this.opts = opts;
|
|
11
15
|
}
|
|
12
16
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* serialized database - which better-sqlite3 takes as its filename - is deserialized there instead.
|
|
17
|
+
* `bun:sqlite` rejects option keys it does not know, so `extensions` is stripped out, and opens only a
|
|
18
|
+
* path, so a serialized database - which better-sqlite3 takes as its filename - is deserialized there instead.
|
|
16
19
|
*/
|
|
17
20
|
async createDb() {
|
|
18
21
|
const { extensions, ...driverOpts } = this.opts ?? {};
|
|
@@ -22,9 +25,9 @@ export class Sqlite3QuerierPool extends AbstractLocalSqliteQuerierPool {
|
|
|
22
25
|
const bunDb = typeof this.filename === 'string'
|
|
23
26
|
? new Database(this.filename, bunOpts)
|
|
24
27
|
: Database.deserialize(this.filename, bunOpts);
|
|
25
|
-
return adaptSqlite(bunDb, (stmt) => stmt.columnNames.length > 0);
|
|
28
|
+
return loadExtensions(adaptSqlite(bunDb, (stmt) => stmt.columnNames.length > 0), extensions);
|
|
26
29
|
}
|
|
27
30
|
const { default: BetterSqlite3 } = await import('better-sqlite3');
|
|
28
|
-
return new BetterSqlite3(this.filename, driverOpts).defaultSafeIntegers(true);
|
|
31
|
+
return loadExtensions(new BetterSqlite3(this.filename, driverOpts).defaultSafeIntegers(true), extensions);
|
|
29
32
|
}
|
|
30
33
|
}
|
|
@@ -5,6 +5,6 @@ import type { SqlDialectFeatures } from '../type/index.js';
|
|
|
5
5
|
* `ORDER BY` inside an aggregate, which its Rust engine lacks. Imports no driver.
|
|
6
6
|
*/
|
|
7
7
|
export declare class TursoDialect extends LibsqlDialect {
|
|
8
|
-
/** The Rust engine takes no `ORDER BY` inside an aggregate. */
|
|
8
|
+
/** The Rust engine takes no `ORDER BY` inside an aggregate, nor a subquery reading the table a write changes. */
|
|
9
9
|
readonly features: SqlDialectFeatures;
|
|
10
10
|
}
|
|
@@ -5,6 +5,10 @@ import { SQLITE_FEATURES } from '../sqlite/sqliteDialect.js';
|
|
|
5
5
|
* `ORDER BY` inside an aggregate, which its Rust engine lacks. Imports no driver.
|
|
6
6
|
*/
|
|
7
7
|
export class TursoDialect extends LibsqlDialect {
|
|
8
|
-
/** The Rust engine takes no `ORDER BY` inside an aggregate. */
|
|
9
|
-
features = {
|
|
8
|
+
/** The Rust engine takes no `ORDER BY` inside an aggregate, nor a subquery reading the table a write changes. */
|
|
9
|
+
features = {
|
|
10
|
+
...SQLITE_FEATURES,
|
|
11
|
+
orderedJsonAggregates: false,
|
|
12
|
+
correlatedWrites: false,
|
|
13
|
+
};
|
|
10
14
|
}
|
|
@@ -2,7 +2,7 @@ import type { VectorDistance, VectorMetric } from '../type/index.js';
|
|
|
2
2
|
import { TursoDialect } from './tursoDialect.js';
|
|
3
3
|
/**
|
|
4
4
|
* SQLite Dialect specialization for the embedded Turso engine, the Rust engine alone: it adds a
|
|
5
|
-
* dot-product distance to libSQL's cosine and L2,
|
|
5
|
+
* dot-product distance to libSQL's cosine and L2, caps no function call, and has no vector index.
|
|
6
6
|
*/
|
|
7
7
|
export declare class TursoLocalDialect extends TursoDialect {
|
|
8
8
|
readonly vectorMetrics: ReadonlyMap<VectorDistance, VectorMetric>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TursoDialect } from './tursoDialect.js';
|
|
2
2
|
/**
|
|
3
3
|
* SQLite Dialect specialization for the embedded Turso engine, the Rust engine alone: it adds a
|
|
4
|
-
* dot-product distance to libSQL's cosine and L2,
|
|
4
|
+
* dot-product distance to libSQL's cosine and L2, caps no function call, and has no vector index.
|
|
5
5
|
*/
|
|
6
6
|
export class TursoLocalDialect extends TursoDialect {
|
|
7
7
|
vectorMetrics = new Map([
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import type { connect } from '@tursodatabase/database';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { AbstractLocalSqliteQuerierPool } from '../sqlite/localSqliteQuerierPool.js';
|
|
3
|
+
import type { SqliteDatabase } from '../sqlite/sqliteQuerier.js';
|
|
4
4
|
import type { ExtraOptions } from '../type/index.js';
|
|
5
5
|
import { TursoLocalDialect } from './tursoLocalDialect.js';
|
|
6
6
|
/** The engine's own options: `readonly`, `timeout`, `encryption`, `experimental` and the rest. */
|
|
7
7
|
export type TursoLocalOptions = NonNullable<Parameters<typeof connect>[1]>;
|
|
8
8
|
/** A pool for the embedded Turso engine, on `uql-orm/turso/local` so its native binaries stay out of edge bundles. */
|
|
9
|
-
export declare class TursoLocalQuerierPool extends
|
|
9
|
+
export declare class TursoLocalQuerierPool extends AbstractLocalSqliteQuerierPool<SqliteDatabase, TursoLocalDialect> {
|
|
10
10
|
readonly filename: string;
|
|
11
11
|
readonly opts?: TursoLocalOptions | undefined;
|
|
12
12
|
constructor(filename?: string, opts?: TursoLocalOptions | undefined, extra?: ExtraOptions);
|
|
13
|
-
protected
|
|
14
|
-
protected buildQuerier(db: SqliteDatabase): SqliteQuerier;
|
|
13
|
+
protected createDb(): Promise<SqliteDatabase>;
|
|
15
14
|
}
|