uql-orm 0.80.0 → 0.82.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 +3 -3
- package/dist/browser/querier/httpQuerier.d.ts +2 -2
- package/dist/browser/querier/httpQuerier.js +2 -1
- package/dist/browser/type/clientQuerier.d.ts +2 -2
- package/dist/browser/uql-browser.min.js +2 -2
- package/dist/browser/uql-browser.min.js.map +9 -8
- package/dist/bunSql/bunSql.util.js +2 -1
- package/dist/dialect/abstractSqlDialect.d.ts +15 -5
- package/dist/dialect/abstractSqlDialect.js +109 -40
- package/dist/dialect/operators.d.ts +7 -1
- package/dist/dialect/operators.js +13 -1
- package/dist/dialect/vectorSqlDialect.d.ts +2 -0
- package/dist/dialect/vectorSqlDialect.js +4 -0
- package/dist/entity/metadata/definition.d.ts +1 -2
- package/dist/entity/metadata/definition.js +37 -39
- package/dist/http/handler.js +5 -4
- package/dist/http/query.d.ts +1 -1
- package/dist/http/query.js +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/migrate/acquireQuerierForMigrations.js +2 -1
- package/dist/migrate/assertCliConfig.js +7 -6
- package/dist/migrate/bin.js +0 -0
- package/dist/migrate/builder/expressions.d.ts +2 -0
- package/dist/migrate/builder/expressions.js +26 -1
- package/dist/migrate/cli-config.js +5 -4
- package/dist/migrate/cli.js +1 -1
- package/dist/migrate/codegen/entityCodeGenerator.js +2 -2
- package/dist/migrate/codegen/indexDecoratorSource.d.ts +3 -2
- package/dist/migrate/codegen/indexDecoratorSource.js +5 -23
- package/dist/migrate/ddl/indexDdl.js +4 -3
- package/dist/migrate/ddl/mssqlTableDdl.d.ts +4 -4
- package/dist/migrate/ddl/mssqlTableDdl.js +20 -14
- package/dist/migrate/ddl/mysqlIndexDdl.d.ts +2 -2
- package/dist/migrate/ddl/mysqlIndexDdl.js +12 -10
- package/dist/migrate/ddl/pgIndexDdl.d.ts +2 -1
- package/dist/migrate/ddl/pgIndexDdl.js +10 -7
- package/dist/migrate/ddl/sqliteIndexDdl.js +2 -1
- package/dist/migrate/ddl/tableDdl.d.ts +3 -2
- package/dist/migrate/ddl/tableDdl.js +11 -8
- package/dist/migrate/drift/driftDetector.d.ts +4 -5
- package/dist/migrate/drift/driftDetector.js +21 -21
- package/dist/migrate/generator/definitionToNode.d.ts +1 -1
- package/dist/migrate/generator/definitionToNode.js +9 -20
- package/dist/migrate/generator/mongoCommand.js +2 -1
- package/dist/migrate/generator/mongoSchemaGenerator.d.ts +1 -1
- package/dist/migrate/generator/mongoSchemaGenerator.js +17 -24
- package/dist/migrate/index.d.ts +2 -1
- package/dist/migrate/index.js +1 -0
- package/dist/migrate/indexPredicate.js +2 -1
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +5 -7
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +13 -19
- package/dist/migrate/introspection/baseSqlIntrospector.js +7 -18
- package/dist/migrate/introspection/mongoIntrospector.d.ts +1 -1
- package/dist/migrate/introspection/mongoIntrospector.js +5 -4
- package/dist/migrate/introspection/mssqlIntrospector.js +2 -1
- package/dist/migrate/introspection/mysqlIntrospector.d.ts +15 -5
- package/dist/migrate/introspection/mysqlIntrospector.js +32 -4
- package/dist/migrate/introspection/postgresIntrospector.d.ts +29 -21
- package/dist/migrate/introspection/postgresIntrospector.js +63 -46
- package/dist/migrate/introspection/sqliteIntrospector.js +11 -9
- package/dist/migrate/migrationTarget.js +2 -1
- package/dist/migrate/migrator.d.ts +5 -0
- package/dist/migrate/migrator.js +35 -45
- package/dist/migrate/schemaChange.d.ts +18 -0
- package/dist/migrate/schemaChange.js +37 -0
- package/dist/migrate/schemaGenerator.d.ts +13 -14
- package/dist/migrate/schemaGenerator.js +88 -181
- package/dist/migrate/triggerSql.d.ts +1 -1
- package/dist/migrate/triggerSql.js +77 -61
- package/dist/mongo/mongoDialect.d.ts +1 -3
- package/dist/mongo/mongoDialect.js +9 -14
- package/dist/mongo/mongodbQuerier.js +3 -7
- package/dist/querier/abstractQuerier.d.ts +9 -4
- package/dist/querier/abstractQuerier.js +26 -19
- package/dist/querier/abstractQuerierPool.d.ts +3 -3
- package/dist/querier/abstractSqlQuerier.d.ts +2 -2
- package/dist/querier/abstractSqlQuerier.js +1 -1
- package/dist/querier/abstractSqlQuerierPool.d.ts +2 -2
- package/dist/querier/queryError.d.ts +2 -2
- package/dist/schema/indexDifferences.d.ts +22 -6
- package/dist/schema/indexDifferences.js +23 -8
- package/dist/schema/matchByKey.d.ts +10 -0
- package/dist/schema/matchByKey.js +18 -0
- package/dist/schema/schemaAST.d.ts +6 -2
- package/dist/schema/schemaAST.js +7 -3
- package/dist/schema/schemaASTBuilder.d.ts +2 -0
- package/dist/schema/schemaASTBuilder.js +17 -11
- package/dist/schema/schemaASTDiffer.d.ts +2 -3
- package/dist/schema/schemaASTDiffer.js +15 -36
- package/dist/schema/types.d.ts +14 -15
- package/dist/type/dialect.d.ts +23 -1
- package/dist/type/entity.d.ts +15 -11
- package/dist/type/logger.d.ts +2 -2
- package/dist/type/migration.d.ts +32 -50
- package/dist/type/querier.d.ts +3 -3
- package/dist/type/query.d.ts +3 -13
- package/dist/type/queryAggregate.d.ts +4 -10
- package/dist/type/queryRaw.d.ts +17 -3
- package/dist/type/queryRaw.js +2 -1
- package/dist/type/queryWhere.d.ts +7 -7
- package/dist/type/universalQuerier.d.ts +3 -3
- package/dist/type/vector.d.ts +2 -1
- package/dist/type/vector.js +2 -1
- package/dist/util/ddlExpression.util.d.ts +5 -1
- package/dist/util/ddlExpression.util.js +6 -2
- package/dist/util/dialect.util.d.ts +13 -5
- package/dist/util/dialect.util.js +28 -20
- package/dist/util/field.util.d.ts +4 -4
- package/dist/util/field.util.js +10 -2
- package/dist/util/hook.util.d.ts +1 -1
- package/dist/util/hook.util.js +8 -1
- package/dist/util/index.d.ts +1 -0
- package/dist/util/index.js +1 -0
- package/dist/util/logger.d.ts +3 -3
- package/dist/util/object.util.js +3 -2
- package/dist/util/raw.d.ts +6 -7
- package/dist/util/raw.js +10 -12
- package/dist/util/sqlLiteral.js +3 -2
- package/dist/util/triggerWrite.d.ts +15 -0
- package/dist/util/triggerWrite.js +20 -0
- package/package.json +1 -1
- package/skills/uql-orm/SKILL.md +3 -3
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import type { IndexSchema } from '../type/index.js';
|
|
1
2
|
import type { IndexNode } from './types.js';
|
|
2
3
|
/**
|
|
3
4
|
* 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
|
+
* `vector` is whether it is a vector index at all, for an engine with one vector index whatever type declared it;
|
|
6
|
+
* `distance` is the metric a vector index was built for.
|
|
5
7
|
* `textIndex` is a text index's weights and language, kept by an engine that lists its fields in no declared order.
|
|
6
8
|
*/
|
|
7
|
-
export type IndexFacet = 'order' | 'nulls' | 'opsClass' | 'accessMethod' | 'include' | 'vector' | 'textIndex';
|
|
9
|
+
export type IndexFacet = 'order' | 'nulls' | 'opsClass' | 'accessMethod' | 'include' | 'vector' | 'distance' | 'textIndex';
|
|
8
10
|
type ComparableIndex = Pick<IndexNode, 'name' | 'entries' | 'unique'>;
|
|
9
11
|
/**
|
|
10
12
|
* Whether the table has this index already, by shape rather than name, uniqueness included. An index
|
|
@@ -17,16 +19,30 @@ export declare function indexSignature(index: ComparableIndex): string;
|
|
|
17
19
|
*/
|
|
18
20
|
export declare function indexNameStem(name: string): string;
|
|
19
21
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
+
* Pairs by name, then what is left by shape: an index the database has under another name is still
|
|
23
|
+
* the one asked for. What stays unpaired is created or dropped.
|
|
22
24
|
*/
|
|
23
|
-
export declare function
|
|
25
|
+
export declare function pairIndexes<S extends ComparableIndex, T extends ComparableIndex>(source: readonly S[], target: readonly T[], normalizeName?: (name: string) => string): {
|
|
26
|
+
created: S[];
|
|
27
|
+
dropped: T[];
|
|
28
|
+
matched: (readonly [S, T])[];
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* The indexes a table lacks, the ones it no longer needs, and the ones to rebuild, differing in what
|
|
32
|
+
* `facets` let the engine report. Only an unpaired index uql named, or whose name the entity claims, is
|
|
33
|
+
* dropped: any other may have been made outside the ORM.
|
|
34
|
+
*/
|
|
35
|
+
export declare function indexChanges<I extends IndexSchema>(table: string, declared: readonly I[], current: readonly IndexNode[], facets: ReadonlySet<IndexFacet>): {
|
|
24
36
|
toAdd: I[];
|
|
25
37
|
toDrop: IndexNode[];
|
|
38
|
+
toAlter: {
|
|
39
|
+
from: IndexNode;
|
|
40
|
+
to: I;
|
|
41
|
+
}[];
|
|
26
42
|
};
|
|
27
43
|
/**
|
|
28
44
|
* What two indexes differ by, comparing only what both sides state structurally: an expression, a JSON
|
|
29
45
|
* path or a predicate is reprinted by the engine, so never compared.
|
|
30
46
|
*/
|
|
31
|
-
export declare function describeIndexDifferences(source:
|
|
47
|
+
export declare function describeIndexDifferences(source: IndexSchema, target: IndexSchema, facets: ReadonlySet<IndexFacet>): string[];
|
|
32
48
|
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { isVectorIndexType } from '../type/vector.js';
|
|
1
|
+
import { indexDistance, isVectorIndexType } from '../type/vector.js';
|
|
2
2
|
import { fulltextConfig } from '../util/dialect.util.js';
|
|
3
3
|
import { derivedIndexName } from '../util/sql.util.js';
|
|
4
|
+
import { matchByKey } from './matchByKey.js';
|
|
4
5
|
/** An entry the engine reprints in its own words, so never compared as written. */
|
|
5
6
|
function isReprinted(entry) {
|
|
6
7
|
return Boolean(entry.expression || entry.jsonPath || entry.jsonArray);
|
|
@@ -26,17 +27,27 @@ export function indexNameStem(name) {
|
|
|
26
27
|
return bare.replace(/__/g, '_');
|
|
27
28
|
}
|
|
28
29
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
30
|
+
* Pairs by name, then what is left by shape: an index the database has under another name is still
|
|
31
|
+
* the one asked for. What stays unpaired is created or dropped.
|
|
31
32
|
*/
|
|
32
|
-
export function
|
|
33
|
-
const
|
|
34
|
-
const
|
|
33
|
+
export function pairIndexes(source, target, normalizeName = (name) => name) {
|
|
34
|
+
const byName = matchByKey(source, target, (index) => normalizeName(indexNameStem(index.name)));
|
|
35
|
+
const byShape = matchByKey(byName.created, byName.dropped, indexSignature);
|
|
36
|
+
return { created: byShape.created, dropped: byShape.dropped, matched: [...byName.matched, ...byShape.matched] };
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The indexes a table lacks, the ones it no longer needs, and the ones to rebuild, differing in what
|
|
40
|
+
* `facets` let the engine report. Only an unpaired index uql named, or whose name the entity claims, is
|
|
41
|
+
* dropped: any other may have been made outside the ORM.
|
|
42
|
+
*/
|
|
43
|
+
export function indexChanges(table, declared, current, facets) {
|
|
44
|
+
const { created, dropped, matched } = pairIndexes(declared, current);
|
|
35
45
|
const claimed = new Set(declared.map((index) => index.name));
|
|
36
46
|
const owned = (index) => claimed.has(index.name) || hasDerivedName(table, index);
|
|
37
47
|
return {
|
|
38
|
-
toAdd:
|
|
39
|
-
toDrop:
|
|
48
|
+
toAdd: created,
|
|
49
|
+
toDrop: dropped.filter(owned),
|
|
50
|
+
toAlter: matched.flatMap(([to, from]) => (describeIndexDifferences(to, from, facets).length ? [{ from, to }] : [])),
|
|
40
51
|
};
|
|
41
52
|
}
|
|
42
53
|
/**
|
|
@@ -85,6 +96,10 @@ export function describeIndexDifferences(source, target, facets) {
|
|
|
85
96
|
if (facets.has('accessMethod') && (source.type ?? 'btree') !== (target.type ?? 'btree')) {
|
|
86
97
|
differences.push(`type: ${target.type ?? 'btree'} -> ${source.type ?? 'btree'}`);
|
|
87
98
|
}
|
|
99
|
+
const bothVector = isVectorIndexType(source.type) && isVectorIndexType(target.type);
|
|
100
|
+
if (facets.has('distance') && bothVector && indexDistance(source) !== indexDistance(target)) {
|
|
101
|
+
differences.push(`distance: ${indexDistance(target)} -> ${indexDistance(source)}`);
|
|
102
|
+
}
|
|
88
103
|
if (facets.has('vector') && isVectorIndexType(source.type) !== isVectorIndexType(target.type)) {
|
|
89
104
|
const [expected, actual] = [source, target].map((index) => (isVectorIndexType(index.type) ? 'yes' : 'no'));
|
|
90
105
|
differences.push(`vector index: ${actual} -> ${expected}`);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The only three ways two keyed collections can differ, which is the shape of every comparison in
|
|
3
|
+
* the schema diff: tables, columns, indexes and relationships all key and then split the same way.
|
|
4
|
+
* One counterpart each, so two items sharing a key (a duplicate index) are not collapsed into one.
|
|
5
|
+
*/
|
|
6
|
+
export declare function matchByKey<S, T>(source: Iterable<S>, target: Iterable<T>, key: (item: S | T) => string): {
|
|
7
|
+
created: S[];
|
|
8
|
+
dropped: T[];
|
|
9
|
+
matched: (readonly [S, T])[];
|
|
10
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The only three ways two keyed collections can differ, which is the shape of every comparison in
|
|
3
|
+
* the schema diff: tables, columns, indexes and relationships all key and then split the same way.
|
|
4
|
+
* One counterpart each, so two items sharing a key (a duplicate index) are not collapsed into one.
|
|
5
|
+
*/
|
|
6
|
+
export function matchByKey(source, target, key) {
|
|
7
|
+
const unpaired = Map.groupBy(target, key);
|
|
8
|
+
const created = [];
|
|
9
|
+
const matched = [];
|
|
10
|
+
for (const item of source) {
|
|
11
|
+
const counterpart = unpaired.get(key(item))?.shift();
|
|
12
|
+
if (counterpart === undefined)
|
|
13
|
+
created.push(item);
|
|
14
|
+
else
|
|
15
|
+
matched.push([item, counterpart]);
|
|
16
|
+
}
|
|
17
|
+
return { created, dropped: [...unpaired.values()].flat(), matched };
|
|
18
|
+
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PrimaryKeySchema } from '../type/migration.js';
|
|
2
|
+
import type { IndexFacet } from './indexDifferences.js';
|
|
3
|
+
import type { ColumnNode, IndexNode, RelationshipNode, TableNode } from './types.js';
|
|
2
4
|
/** A table node with its collections empty, ready to be filled. */
|
|
3
|
-
export declare function createTableNode(name: string, schema?: string,
|
|
5
|
+
export declare function createTableNode(name: string, schema?: string, indexFacets?: ReadonlySet<IndexFacet>): TableNode;
|
|
6
|
+
/** The key the columns' own flags say, in their order, for a source that reports no key of its own. */
|
|
7
|
+
export declare function keyOfColumns(columns: Iterable<Pick<ColumnNode, 'name' | 'isPrimaryKey'>>): PrimaryKeySchema | undefined;
|
|
4
8
|
/** A database schema as a graph: tables, the foreign keys between them, and their indexes. */
|
|
5
9
|
export declare class SchemaAST {
|
|
6
10
|
readonly tables: Map<string, TableNode>;
|
package/dist/schema/schemaAST.js
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import { qualifyName } from '../util/sql.util.js';
|
|
2
2
|
import { createOrder, dropOrder } from './dependencyGraph.js';
|
|
3
3
|
/** A table node with its collections empty, ready to be filled. */
|
|
4
|
-
export function createTableNode(name, schema,
|
|
4
|
+
export function createTableNode(name, schema, indexFacets = new Set()) {
|
|
5
5
|
return {
|
|
6
6
|
name,
|
|
7
7
|
schema,
|
|
8
|
-
|
|
8
|
+
indexFacets,
|
|
9
9
|
columns: new Map(),
|
|
10
|
-
primaryKey: [],
|
|
11
10
|
indexes: [],
|
|
12
11
|
checks: [],
|
|
13
12
|
incomingRelations: [],
|
|
14
13
|
outgoingRelations: [],
|
|
15
14
|
};
|
|
16
15
|
}
|
|
16
|
+
/** The key the columns' own flags say, in their order, for a source that reports no key of its own. */
|
|
17
|
+
export function keyOfColumns(columns) {
|
|
18
|
+
const keyColumns = [...columns].filter((column) => column.isPrimaryKey).map((column) => column.name);
|
|
19
|
+
return keyColumns.length ? { columns: keyColumns } : undefined;
|
|
20
|
+
}
|
|
17
21
|
/** A database schema as a graph: tables, the foreign keys between them, and their indexes. */
|
|
18
22
|
export class SchemaAST {
|
|
19
23
|
tables = new Map();
|
|
@@ -25,6 +25,8 @@ export interface BuildSchemaASTOptions {
|
|
|
25
25
|
compileIndexPredicate?: (where: EntityWhereMeta<object>, entity: Type<object>, indexName: string) => string;
|
|
26
26
|
/** Whether a weighted fulltext index declares one of its own for each heavier column, as MySQL scores through one. */
|
|
27
27
|
textScoreIndexes?: boolean;
|
|
28
|
+
/** Whether a column a vector index covers is `NOT NULL` whatever the entity declares, as MariaDB demands. */
|
|
29
|
+
vectorIndexRequiresNotNull?: boolean;
|
|
28
30
|
}
|
|
29
31
|
/**
|
|
30
32
|
* Build a SchemaAST from entity classes (decorated with `@Entity`, `@Field`, etc.).
|
|
@@ -4,8 +4,9 @@ import { fulltextWeights, textWeightSteps } from '../util/dialect.util.js';
|
|
|
4
4
|
import { isAutoIncrement, isInlinedExpression, isSoleIdField } from '../util/field.util.js';
|
|
5
5
|
import { definedEntries } from '../util/object.util.js';
|
|
6
6
|
import { derivedForeignKeyName, derivedIndexName, qualifyName } from '../util/sql.util.js';
|
|
7
|
+
import { UqlUsageError } from '../util/uqlError.js';
|
|
7
8
|
import { resolveColumnCanonicalType } from './canonicalType.js';
|
|
8
|
-
import { createTableNode, SchemaAST } from './schemaAST.js';
|
|
9
|
+
import { createTableNode, keyOfColumns, SchemaAST } from './schemaAST.js';
|
|
9
10
|
import { DEFAULT_FOREIGN_KEY_ACTION } from './types.js';
|
|
10
11
|
/**
|
|
11
12
|
* Build a SchemaAST from entity classes (decorated with `@Entity`, `@Field`, etc.).
|
|
@@ -26,6 +27,7 @@ export function buildSchemaAST(entities, options = {}) {
|
|
|
26
27
|
compileDdl,
|
|
27
28
|
compileIndexPredicate: options.compileIndexPredicate ?? compileDdl,
|
|
28
29
|
textScoreIndexes: options.textScoreIndexes ?? false,
|
|
30
|
+
vectorIndexRequiresNotNull: options.vectorIndexRequiresNotNull ?? false,
|
|
29
31
|
};
|
|
30
32
|
for (const pass of [addTableFromEntity, addRelationshipsFromEntity, addIndexesFromEntity]) {
|
|
31
33
|
for (const entity of entities) {
|
|
@@ -36,7 +38,13 @@ export function buildSchemaAST(entities, options = {}) {
|
|
|
36
38
|
}
|
|
37
39
|
/** The `compileDdl` of a build given no dialect, which has nothing to render an entity's SQL with. */
|
|
38
40
|
function refuseDdl() {
|
|
39
|
-
throw new
|
|
41
|
+
throw new UqlUsageError('building the schema of an entity that declares SQL (a check, a stored computed column, an index expression or predicate) needs a dialect to render it: pass `compileDdl`, as `buildEntityAST` does');
|
|
42
|
+
}
|
|
43
|
+
/** The entries a vector index of `meta` covers: the members it names, and any expression. */
|
|
44
|
+
function vectorIndexedEntries(meta) {
|
|
45
|
+
return new Set((meta.indexes ?? [])
|
|
46
|
+
.filter((index) => index.type === 'vector')
|
|
47
|
+
.flatMap((index) => index.columns.map((entry) => entry.column)));
|
|
40
48
|
}
|
|
41
49
|
/**
|
|
42
50
|
* Add a table from entity metadata.
|
|
@@ -44,8 +52,9 @@ function refuseDdl() {
|
|
|
44
52
|
function addTableFromEntity(ctx, meta) {
|
|
45
53
|
const tableName = ctx.resolveTableName(meta);
|
|
46
54
|
const table = createTableNode(tableName, ctx.resolveSchema(meta));
|
|
47
|
-
const { columns
|
|
48
|
-
table.checks
|
|
55
|
+
const { columns } = table;
|
|
56
|
+
table.checks.push(...(meta.checks ?? []).map(({ name, where }) => ({ name, expression: ctx.compileDdl(where, meta.entity) })));
|
|
57
|
+
const notNull = ctx.vectorIndexRequiresNotNull ? vectorIndexedEntries(meta) : new Set();
|
|
49
58
|
// Add columns from fields
|
|
50
59
|
for (const [key, field] of definedEntries(meta.fields)) {
|
|
51
60
|
// An inlined expression has no column; a stored one is a column like any other.
|
|
@@ -60,7 +69,7 @@ function addTableFromEntity(ctx, meta) {
|
|
|
60
69
|
type,
|
|
61
70
|
// A primary key is NOT NULL in every engine, whatever the entity's property says: `id?: number`
|
|
62
71
|
// is optional because the database assigns it, not because the column accepts a null.
|
|
63
|
-
nullable: isPrimaryKey ? false : (field.nullable ?? true),
|
|
72
|
+
nullable: isPrimaryKey || notNull.has(key) ? false : (field.nullable ?? true),
|
|
64
73
|
defaultValue: field.defaultValue,
|
|
65
74
|
isPrimaryKey,
|
|
66
75
|
isAutoIncrement: isAutoIncrement(field, isSoleKey),
|
|
@@ -74,10 +83,8 @@ function addTableFromEntity(ctx, meta) {
|
|
|
74
83
|
references: undefined,
|
|
75
84
|
};
|
|
76
85
|
columns.set(columnName, column);
|
|
77
|
-
if (field.isId) {
|
|
78
|
-
primaryKey.push(column);
|
|
79
|
-
}
|
|
80
86
|
}
|
|
87
|
+
table.primaryKey = keyOfColumns(columns.values());
|
|
81
88
|
ctx.ast.addTable(table);
|
|
82
89
|
}
|
|
83
90
|
/** The node an entity maps to, found under the key {@link SchemaAST} stores it by. */
|
|
@@ -157,11 +164,10 @@ function addForeignKeyIndexes(ctx, meta, table) {
|
|
|
157
164
|
});
|
|
158
165
|
}
|
|
159
166
|
}
|
|
160
|
-
/** Whether the key
|
|
167
|
+
/** Whether the key or an index already leads with `columns`, which is all a lookup needs. */
|
|
161
168
|
function isIndexedBy(table, columns) {
|
|
162
169
|
const leads = (indexed) => columns.every((column, at) => indexed[at] === column);
|
|
163
|
-
return (leads(table.primaryKey
|
|
164
|
-
(columns.length === 1 && table.columns.get(columns[0])?.isUnique === true) ||
|
|
170
|
+
return (leads(table.primaryKey?.columns ?? []) ||
|
|
165
171
|
table.indexes.some((index) => leads(index.entries.map((entry) => entry.expression || entry.jsonPath || entry.jsonArray ? undefined : entry.column))));
|
|
166
172
|
}
|
|
167
173
|
/** An `include` column is named like any other, so a naming strategy has to reach it too. */
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { type IndexFacet } from './indexDifferences.js';
|
|
2
1
|
import type { SchemaAST } from './schemaAST.js';
|
|
3
2
|
import type { CanonicalType } from './types.js';
|
|
4
3
|
import type { ColumnDiff, ForeignKeyAction, IndexDiff, RelationshipDiff, RelationshipNode, SchemaDiffResult, TableDiff, TableNode } from './types.js';
|
|
@@ -8,8 +7,6 @@ import type { ColumnDiff, ForeignKeyAction, IndexDiff, RelationshipDiff, Relatio
|
|
|
8
7
|
export interface DiffOptions {
|
|
9
8
|
/** Compare indexes */
|
|
10
9
|
compareIndexes?: boolean;
|
|
11
|
-
/** What the target side can report about an index, normally an introspector's `indexFacets`. Anything left out is not compared. */
|
|
12
|
-
indexFacets?: ReadonlySet<IndexFacet>;
|
|
13
10
|
/** Compare foreign keys/relationships */
|
|
14
11
|
compareRelationships?: boolean;
|
|
15
12
|
/** Ignore case differences in names */
|
|
@@ -38,3 +35,5 @@ export declare function referentialActions(rel: RelationshipNode): {
|
|
|
38
35
|
readonly onDelete: ForeignKeyAction;
|
|
39
36
|
readonly onUpdate: ForeignKeyAction;
|
|
40
37
|
};
|
|
38
|
+
/** Two defaults compared as written, where no dialect reprints them: `now()` and `CURRENT_TIMESTAMP` are one. */
|
|
39
|
+
export declare function defaultsEqualAsWritten(expected: unknown, actual: unknown): boolean;
|
|
@@ -1,37 +1,21 @@
|
|
|
1
1
|
import { areTypesEqual, isBreakingTypeChange } from './canonicalType.js';
|
|
2
|
-
import { describeIndexDifferences,
|
|
2
|
+
import { describeIndexDifferences, pairIndexes } from './indexDifferences.js';
|
|
3
|
+
import { matchByKey } from './matchByKey.js';
|
|
3
4
|
import { DEFAULT_FOREIGN_KEY_ACTION } from './types.js';
|
|
4
5
|
/**
|
|
5
6
|
* Default diff options.
|
|
6
7
|
*/
|
|
7
8
|
const DEFAULT_OPTIONS = {
|
|
8
9
|
compareIndexes: true,
|
|
9
|
-
indexFacets: new Set(),
|
|
10
10
|
compareRelationships: true,
|
|
11
11
|
normalizeType: (type) => type,
|
|
12
|
-
defaultsEqual:
|
|
12
|
+
defaultsEqual: defaultsEqualAsWritten,
|
|
13
13
|
ignoreCase: false,
|
|
14
14
|
excludeTables: [],
|
|
15
15
|
};
|
|
16
16
|
function nameNormalizer(opts) {
|
|
17
17
|
return opts.ignoreCase ? (name) => name.toLowerCase() : (name) => name;
|
|
18
18
|
}
|
|
19
|
-
/**
|
|
20
|
-
* The only three ways two keyed collections can differ, which is the shape of every comparison here:
|
|
21
|
-
* tables, columns, indexes and relationships all key by name and then split the same way.
|
|
22
|
-
*/
|
|
23
|
-
function matchByKey(source, target, key) {
|
|
24
|
-
const sourceByKey = new Map([...source].map((item) => [key(item), item]));
|
|
25
|
-
const targetByKey = new Map([...target].map((item) => [key(item), item]));
|
|
26
|
-
return {
|
|
27
|
-
created: [...sourceByKey].filter(([at]) => !targetByKey.has(at)).map(([, item]) => item),
|
|
28
|
-
dropped: [...targetByKey].filter(([at]) => !sourceByKey.has(at)).map(([, item]) => item),
|
|
29
|
-
matched: [...sourceByKey].flatMap(([at, item]) => {
|
|
30
|
-
const counterpart = targetByKey.get(at);
|
|
31
|
-
return counterpart ? [[item, counterpart]] : [];
|
|
32
|
-
}),
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
19
|
/** How a relationship diff names the pair it is about, whichever way it differs. */
|
|
36
20
|
function relationEnds(relation) {
|
|
37
21
|
return { name: relation.name, fromTable: relation.from.table.name, toTable: relation.to.table.name };
|
|
@@ -86,12 +70,12 @@ export function diffTable(source, target, options = {}) {
|
|
|
86
70
|
}
|
|
87
71
|
/** The two keys where they hold different columns, compared in order and never by the name the engine gave them. */
|
|
88
72
|
function diffPrimaryKey(source, target) {
|
|
89
|
-
const expected = source.primaryKey
|
|
90
|
-
const actual = target.primaryKey
|
|
73
|
+
const expected = source.primaryKey?.columns ?? [];
|
|
74
|
+
const actual = target.primaryKey?.columns ?? [];
|
|
91
75
|
if (expected.length === actual.length && expected.every((column, i) => column === actual[i])) {
|
|
92
76
|
return undefined;
|
|
93
77
|
}
|
|
94
|
-
return { table: source.name, expected, actual
|
|
78
|
+
return { table: source.name, expected: source.primaryKey, actual: target.primaryKey };
|
|
95
79
|
}
|
|
96
80
|
/**
|
|
97
81
|
* Compare columns between two tables.
|
|
@@ -120,17 +104,14 @@ function diffTableColumns(source, target, opts) {
|
|
|
120
104
|
.filter((diff) => diff !== undefined),
|
|
121
105
|
];
|
|
122
106
|
}
|
|
123
|
-
/**
|
|
124
|
-
* Compare indexes between two tables.
|
|
125
|
-
*/
|
|
107
|
+
/** Compare indexes between two tables, paired by {@link pairIndexes}, in what the target's reader reports. */
|
|
126
108
|
function diffTableIndexes(source, target, opts) {
|
|
127
|
-
const
|
|
128
|
-
const { created, dropped, matched } = matchByKey(source.indexes, target.indexes, (index) => normalizeName(indexNameStem(index.name)));
|
|
109
|
+
const { created, dropped, matched } = pairIndexes(source.indexes, target.indexes, nameNormalizer(opts));
|
|
129
110
|
return [
|
|
130
111
|
...created.map((index) => ({ name: index.name, table: source.name, type: 'create', expected: index })),
|
|
131
112
|
...dropped.map((index) => ({ name: index.name, table: target.name, type: 'drop', actual: index })),
|
|
132
113
|
...matched
|
|
133
|
-
.map(([sourceIndex, targetIndex]) => diffIndex(source.name, sourceIndex, targetIndex,
|
|
114
|
+
.map(([sourceIndex, targetIndex]) => diffIndex(source.name, sourceIndex, targetIndex, target.indexFacets))
|
|
134
115
|
.filter((diff) => diff !== undefined),
|
|
135
116
|
];
|
|
136
117
|
}
|
|
@@ -158,12 +139,9 @@ function diffColumn(tableName, source, target, opts) {
|
|
|
158
139
|
if (!impliedNotNull && source.nullable !== target.nullable) {
|
|
159
140
|
differences.push(`nullable: ${target.nullable} -> ${source.nullable}`);
|
|
160
141
|
}
|
|
161
|
-
// Compare unique constraint
|
|
162
|
-
if (source.isUnique !== target.isUnique) {
|
|
163
|
-
differences.push(`unique: ${target.isUnique} -> ${source.isUnique}`);
|
|
164
|
-
}
|
|
165
142
|
// Not compared, since no statement this generator emits could settle a difference: `isAutoIncrement`,
|
|
166
|
-
// `enum` (a check the database reprints), `generatedAs`, and `comment`.
|
|
143
|
+
// `enum` (a check the database reprints), `generatedAs`, and `comment`. Nor `isUnique`: a unique
|
|
144
|
+
// column is a unique index, compared with the indexes.
|
|
167
145
|
// Compare default values (if both defined)
|
|
168
146
|
if (!opts.defaultsEqual(source.defaultValue, target.defaultValue)) {
|
|
169
147
|
differences.push(`default: ${target.defaultValue ?? 'NULL'} -> ${source.defaultValue ?? 'NULL'}`);
|
|
@@ -262,9 +240,10 @@ function formatType(type) {
|
|
|
262
240
|
result += ' unsigned';
|
|
263
241
|
return result;
|
|
264
242
|
}
|
|
265
|
-
/**
|
|
266
|
-
|
|
267
|
-
|
|
243
|
+
/** Two defaults compared as written, where no dialect reprints them: `now()` and `CURRENT_TIMESTAMP` are one. */
|
|
244
|
+
export function defaultsEqualAsWritten(expected, actual) {
|
|
245
|
+
return normalizeDefault(expected) === normalizeDefault(actual);
|
|
246
|
+
}
|
|
268
247
|
function normalizeDefault(value) {
|
|
269
248
|
if (value === undefined || value === null)
|
|
270
249
|
return '';
|
package/dist/schema/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { IndexSchema } from '../type/migration.js';
|
|
1
|
+
import type { IndexSchema, PrimaryKeySchema } from '../type/migration.js';
|
|
2
|
+
import type { IndexFacet } from './indexDifferences.js';
|
|
2
3
|
/**
|
|
3
4
|
* Type categories universal across SQL dialects.
|
|
4
5
|
* These represent logical/semantic types, not specific SQL types.
|
|
@@ -109,18 +110,17 @@ export interface TableNode {
|
|
|
109
110
|
readonly schema?: string;
|
|
110
111
|
/** Map of column name to column node */
|
|
111
112
|
readonly columns: Map<string, ColumnNode>;
|
|
112
|
-
/**
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* What the constraint is called, where a name is known: read back from the database on an
|
|
116
|
-
* introspected table, absent on one built from entities, where nothing has named it yet. A `DROP`
|
|
117
|
-
* is the only thing that needs it - see {@link TableSchema.primaryKeyName}.
|
|
118
|
-
*/
|
|
119
|
-
primaryKeyName?: string;
|
|
113
|
+
/** The table's key, named where the database reported a name; none on a table without one. */
|
|
114
|
+
primaryKey?: PrimaryKeySchema;
|
|
120
115
|
/** Indexes on this table */
|
|
121
116
|
readonly indexes: IndexNode[];
|
|
122
|
-
/**
|
|
123
|
-
|
|
117
|
+
/**
|
|
118
|
+
* What the introspector that read this table reports about an index, and so all an index diff against
|
|
119
|
+
* it may compare. None on a table built from entities.
|
|
120
|
+
*/
|
|
121
|
+
readonly indexFacets: ReadonlySet<IndexFacet>;
|
|
122
|
+
/** `CHECK` constraints on this table. */
|
|
123
|
+
readonly checks: CheckSchema[];
|
|
124
124
|
/** Optional table comment */
|
|
125
125
|
readonly comment?: string;
|
|
126
126
|
/** Relationships pointing TO this table (other tables referencing this one) */
|
|
@@ -214,10 +214,9 @@ export interface TableDiff {
|
|
|
214
214
|
*/
|
|
215
215
|
export interface PrimaryKeyDiff {
|
|
216
216
|
readonly table: string;
|
|
217
|
-
readonly expected
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
readonly actualName?: string;
|
|
217
|
+
readonly expected?: PrimaryKeySchema;
|
|
218
|
+
/** Named as the database reported it, which is the only name a `DROP` can use. */
|
|
219
|
+
readonly actual?: PrimaryKeySchema;
|
|
221
220
|
}
|
|
222
221
|
/**
|
|
223
222
|
* Difference between two index definitions.
|
package/dist/type/dialect.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { EntityMeta, UpdatePayload } from './entity.js';
|
|
1
|
+
import type { EntityMeta, EntityPredicate, UpdatePayload } from './entity.js';
|
|
2
2
|
import type { Query, QueryConflictPaths, QueryPage, QueryRenderOptions, QuerySearch, RelationQuery } from './query.js';
|
|
3
3
|
import type { QueryAggMap, QueryAggregate, QueryAggregateOp, QueryGroupMap } from './queryAggregate.js';
|
|
4
|
+
import type { QueryRawRenderOptions } from './queryRaw.js';
|
|
4
5
|
import type { QueryWhere } from './queryWhere.js';
|
|
5
6
|
import type { Type } from './utility.js';
|
|
6
7
|
import type { QueryVectorQuery } from './vector.js';
|
|
@@ -231,6 +232,25 @@ export interface TriggerFeatures {
|
|
|
231
232
|
*/
|
|
232
233
|
readonly before: boolean;
|
|
233
234
|
}
|
|
235
|
+
/** Where DDL's SQL sits: the row a trigger's predicate reads, as its prefix, and a set-based body's rows. */
|
|
236
|
+
export type DdlRenderOptions = Pick<QueryComparisonOptions, 'escapedPrefix' | 'operand'> & Pick<QueryRawRenderOptions, 'rows'>;
|
|
237
|
+
/**
|
|
238
|
+
* A write a trigger's body runs, as `insertInto`, `updateTable` and `deleteFrom` state it. Held untyped
|
|
239
|
+
* here, past those helpers' typing, since the dialect renders it by the entity's metadata alone.
|
|
240
|
+
*/
|
|
241
|
+
export type TriggerWrite = {
|
|
242
|
+
readonly entity: Type<object>;
|
|
243
|
+
} & ({
|
|
244
|
+
readonly kind: 'insert';
|
|
245
|
+
readonly row: Readonly<Record<string, unknown>>;
|
|
246
|
+
} | {
|
|
247
|
+
readonly kind: 'update';
|
|
248
|
+
readonly set: Readonly<Record<string, unknown>>;
|
|
249
|
+
readonly where: EntityPredicate<object>;
|
|
250
|
+
} | {
|
|
251
|
+
readonly kind: 'delete';
|
|
252
|
+
readonly where: EntityPredicate<object>;
|
|
253
|
+
});
|
|
234
254
|
/**
|
|
235
255
|
* What a SQL statement is rendered through, as a `raw` callback and a query context see it:
|
|
236
256
|
* `AbstractSqlDialect` is the one implementation.
|
|
@@ -262,6 +282,8 @@ export interface SqlQueryDialect {
|
|
|
262
282
|
update<E>(ctx: QueryContext, entity: Type<E>, q: QuerySearch<E>, payload: UpdatePayload<E>, opts?: QueryRenderOptions): void;
|
|
263
283
|
/** An upsert of one record or many by their conflict paths. */
|
|
264
284
|
upsert<E>(ctx: QueryContext, entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: E | E[]): void;
|
|
285
|
+
/** A write in a trigger's body; `rows` is where a set-based engine's body reads its rows from. */
|
|
286
|
+
triggerWrite(ctx: QueryContext, write: TriggerWrite, rows?: string): void;
|
|
265
287
|
/** A delete of the records the query matches, a soft delete where the entity has one. */
|
|
266
288
|
delete<E>(ctx: QueryContext, entity: Type<E>, q: QuerySearch<E>, opts?: QueryRenderOptions): void;
|
|
267
289
|
/**
|
package/dist/type/entity.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { EnumValues, ForeignKeyAction, IndexType } from '../schema/types.js';
|
|
2
2
|
import type { SqlDialectName } from './dialect.js';
|
|
3
3
|
import type { FilterOptions, RelationQuery } from './query.js';
|
|
4
|
-
import type { ColumnRef, QueryRaw, RelationAggregate } from './queryRaw.js';
|
|
4
|
+
import type { ColumnRef, QueryRaw, RawFor, RelationAggregate } from './queryRaw.js';
|
|
5
5
|
import type { QueryWhere } from './queryWhere.js';
|
|
6
6
|
import type { AtLeastOne, Except, ExactlyOne, IsEqual, IsMany, Json, Scalar, Type, Unpacked, Writable } from './utility.js';
|
|
7
7
|
import type { VectorDistance, VectorIndexOptions, VectorIndexType } from './vector.js';
|
|
@@ -37,6 +37,10 @@ export type WritableKey<E> = {
|
|
|
37
37
|
}[FieldKey<E>];
|
|
38
38
|
/** A whole-record write as a caller supplies one: {@link EntityData} without the fields it cannot write. */
|
|
39
39
|
export type EntityWrite<E> = EntityData<E, WritableKey<E>>;
|
|
40
|
+
/** A row a trigger's body writes: each writable field its value, or SQL - a row's ref most often. */
|
|
41
|
+
export type WriteRow<E, F extends keyof E = WritableKey<E>> = {
|
|
42
|
+
readonly [K in F]?: E[K] | RawFor<QueryRaw, E[K]>;
|
|
43
|
+
};
|
|
40
44
|
/**
|
|
41
45
|
* The property an entity brands with {@link versionKey} as its optimistic lock, `never` where it
|
|
42
46
|
* brands none. The brand is what carries `@Field({ version: true })` to the type level, since a
|
|
@@ -114,7 +118,7 @@ export type JsonArrayFields<T> = {
|
|
|
114
118
|
*/
|
|
115
119
|
export type JsonUpdateOp<T = unknown> = {
|
|
116
120
|
readonly $set?: Partial<T>;
|
|
117
|
-
readonly $unset?: unknown extends T ? string[] : (keyof T & string)[];
|
|
121
|
+
readonly $unset?: unknown extends T ? readonly string[] : readonly (keyof T & string)[];
|
|
118
122
|
readonly $push?: JsonArrayFields<T>;
|
|
119
123
|
readonly $pull?: JsonArrayFields<T>;
|
|
120
124
|
};
|
|
@@ -133,7 +137,7 @@ export type FieldUpdateOp<T extends number | bigint = number | bigint> = Exactly
|
|
|
133
137
|
/** The {@link FieldUpdateOp} a field takes: `never` on one it has no operator for, which is any but a number. */
|
|
134
138
|
type FieldUpdateOpFor<V> = [NonNullable<V>] extends [number] ? FieldUpdateOp<number> : [NonNullable<V>] extends [bigint] ? FieldUpdateOp<bigint> : never;
|
|
135
139
|
/** What an update takes beyond the value: `null` to clear an optional member, `raw` SQL, and update operators. */
|
|
136
|
-
type UpdateExtra<V, Raw> = (undefined extends V ? null : never) | Raw | JsonUpdateOpFor<V> | FieldUpdateOpFor<V>;
|
|
140
|
+
type UpdateExtra<V, Raw> = (undefined extends V ? null : never) | RawFor<Raw, V> | JsonUpdateOpFor<V> | FieldUpdateOpFor<V>;
|
|
137
141
|
/**
|
|
138
142
|
* What a whole-record write persists: the fields and relations with their declared optionality, a
|
|
139
143
|
* related row's alike, and no methods. Two mapped types, since asking each key costs a conditional.
|
|
@@ -411,6 +415,10 @@ export type RelationReference<O, E> = {
|
|
|
411
415
|
readonly foreign: F;
|
|
412
416
|
};
|
|
413
417
|
}[FieldKey<E>];
|
|
418
|
+
/** The fields of `E` whose value is a `T`, however optional: `FieldKeyOf<E, number | bigint>` are the ones a sum adds up. */
|
|
419
|
+
export type FieldKeyOf<E, T> = {
|
|
420
|
+
readonly [K in FieldKey<E>]-?: [NonNullable<E[K]>] extends [T] ? K : never;
|
|
421
|
+
}[FieldKey<E>];
|
|
414
422
|
/** The fields of `O` that can hold any value `V` takes. */
|
|
415
423
|
type FieldKeyHolding<O, V> = {
|
|
416
424
|
readonly [K in keyof O]-?: [NonNullable<V>] extends [NonNullable<O[K]>] ? K : never;
|
|
@@ -446,16 +454,12 @@ export type KeyMap<E> = {
|
|
|
446
454
|
};
|
|
447
455
|
/** The fields of `E` as {@link ColumnRef}s, for SQL that names them: `refs(User)`, or a definition's callback. */
|
|
448
456
|
export type RefMap<E, F extends keyof E = FieldKey<E>> = {
|
|
449
|
-
readonly [K in F]-?: ColumnRef<K & string>;
|
|
457
|
+
readonly [K in F]-?: ColumnRef<K & string, E[K]>;
|
|
450
458
|
};
|
|
451
459
|
/** SQL a definition writes: `raw`, or a callback reading the fields off its refs, bivariant so the registry can hold it. */
|
|
452
460
|
export type EntitySql<E> = QueryRaw | {
|
|
453
461
|
sql(refs: RefMap<E>): QueryRaw;
|
|
454
462
|
}['sql'];
|
|
455
|
-
/** The fields of `C` a `sum` or an `avg` can add up. */
|
|
456
|
-
type NumericKey<C> = {
|
|
457
|
-
readonly [K in FieldKey<C>]-?: [NonNullable<C[K]>] extends [number | bigint] ? K : never;
|
|
458
|
-
}[FieldKey<C>];
|
|
459
463
|
/** One field of `C`, read off its refs: `(item) => item.amount`. */
|
|
460
464
|
type PickRef<C, K extends keyof C> = (refs: RefMap<C>) => ColumnRef<K & string>;
|
|
461
465
|
/**
|
|
@@ -466,11 +470,11 @@ type PickRef<C, K extends keyof C> = (refs: RefMap<C>) => ColumnRef<K & string>;
|
|
|
466
470
|
export type RelationRef<C> = {
|
|
467
471
|
count(q?: AggregateFilter<C>): RelationAggregate<number, true>;
|
|
468
472
|
count(q: AggregatePage<C>): RelationAggregate<number, false>;
|
|
469
|
-
sum<K extends
|
|
470
|
-
sum<K extends
|
|
473
|
+
sum<K extends FieldKeyOf<C, number | bigint>>(pick: PickRef<C, K>, q?: AggregateFilter<C>): RelationAggregate<NonNullable<C[K]>, true>;
|
|
474
|
+
sum<K extends FieldKeyOf<C, number | bigint>>(pick: PickRef<C, K>, q: AggregateTopRows<C>): RelationAggregate<NonNullable<C[K]>, false>;
|
|
471
475
|
min<K extends FieldKey<C>>(pick: PickRef<C, K>, q?: AggregateRows<C>): RelationAggregate<NonNullable<C[K]> | null, false>;
|
|
472
476
|
max<K extends FieldKey<C>>(pick: PickRef<C, K>, q?: AggregateRows<C>): RelationAggregate<NonNullable<C[K]> | null, false>;
|
|
473
|
-
avg<K extends
|
|
477
|
+
avg<K extends FieldKeyOf<C, number | bigint>>(pick: PickRef<C, K>, q?: AggregateRows<C>): RelationAggregate<number | null, false>;
|
|
474
478
|
};
|
|
475
479
|
/**
|
|
476
480
|
* What an aggregate reads of the related rows. The predicate is an {@link EntityPredicate} rather than a
|
package/dist/type/logger.d.ts
CHANGED
|
@@ -12,9 +12,9 @@ export interface Logger {
|
|
|
12
12
|
* @param values - The parameters passed to the query.
|
|
13
13
|
* @param duration - The time it took to execute the query in milliseconds.
|
|
14
14
|
*/
|
|
15
|
-
logQuery?(query: string, values?: unknown[], duration?: number): void;
|
|
15
|
+
logQuery?(query: string, values?: readonly unknown[], duration?: number): void;
|
|
16
16
|
/** Logs a query that took longer than the threshold, its values `undefined` unless `logValues` is on. */
|
|
17
|
-
logSlowQuery?(query: string, values?: unknown[], duration?: number): void;
|
|
17
|
+
logSlowQuery?(query: string, values?: readonly unknown[], duration?: number): void;
|
|
18
18
|
/**
|
|
19
19
|
* Logs a warning.
|
|
20
20
|
*/
|