uql-orm 0.79.0 → 0.81.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/dist/browser/uql-browser.min.js.map +2 -2
- package/dist/cockroachdb/cockroachDialect.js +5 -1
- package/dist/dialect/abstractDialect.d.ts +1 -31
- package/dist/dialect/abstractDialect.js +3 -27
- package/dist/dialect/abstractSqlDialect.d.ts +25 -56
- package/dist/dialect/abstractSqlDialect.js +78 -145
- package/dist/dialect/aliases.d.ts +5 -0
- package/dist/dialect/aliases.js +5 -0
- package/dist/dialect/mysqlLikeSqlDialect.d.ts +4 -2
- package/dist/dialect/mysqlLikeSqlDialect.js +14 -1
- package/dist/dialect/operators.d.ts +66 -0
- package/dist/dialect/operators.js +129 -0
- package/dist/dialect/pgLikeSqlDialect.d.ts +4 -1
- package/dist/dialect/pgLikeSqlDialect.js +16 -3
- package/dist/dialect/vectorSqlDialect.d.ts +2 -0
- package/dist/dialect/vectorSqlDialect.js +4 -0
- package/dist/entity/decorator/entity.d.ts +6 -1
- package/dist/entity/decorator/entity.js +12 -1
- package/dist/entity/index.d.ts +1 -1
- package/dist/entity/index.js +1 -1
- package/dist/entity/metadata/definition.d.ts +6 -1
- package/dist/entity/metadata/definition.js +19 -0
- package/dist/migrate/builder/expressions.d.ts +2 -0
- package/dist/migrate/builder/expressions.js +24 -0
- package/dist/migrate/cli.js +1 -1
- package/dist/migrate/codegen/entityCodeGenerator.js +2 -2
- package/dist/migrate/codegen/entityTypes.js +1 -2
- package/dist/migrate/codegen/indexDecoratorSource.d.ts +3 -2
- package/dist/migrate/codegen/indexDecoratorSource.js +5 -23
- package/dist/migrate/ddl/mssqlIndexDdl.d.ts +5 -0
- package/dist/migrate/ddl/mssqlIndexDdl.js +10 -0
- package/dist/migrate/ddl/mssqlTableDdl.d.ts +6 -4
- package/dist/migrate/ddl/mssqlTableDdl.js +25 -14
- package/dist/migrate/ddl/mysqlIndexDdl.d.ts +2 -2
- package/dist/migrate/ddl/mysqlIndexDdl.js +7 -6
- package/dist/migrate/ddl/pgIndexDdl.d.ts +2 -1
- package/dist/migrate/ddl/pgIndexDdl.js +8 -6
- package/dist/migrate/ddl/tableDdl.d.ts +5 -2
- package/dist/migrate/ddl/tableDdl.js +13 -7
- 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/mongoSchemaGenerator.d.ts +5 -1
- package/dist/migrate/generator/mongoSchemaGenerator.js +20 -18
- package/dist/migrate/index.d.ts +2 -1
- package/dist/migrate/index.js +1 -0
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +16 -6
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +18 -4
- package/dist/migrate/introspection/baseSqlIntrospector.js +7 -18
- package/dist/migrate/introspection/mongoIntrospector.d.ts +3 -1
- package/dist/migrate/introspection/mongoIntrospector.js +7 -3
- package/dist/migrate/introspection/mssqlIntrospector.d.ts +1 -0
- package/dist/migrate/introspection/mssqlIntrospector.js +9 -0
- package/dist/migrate/introspection/mysqlIntrospector.d.ts +16 -5
- package/dist/migrate/introspection/mysqlIntrospector.js +39 -2
- package/dist/migrate/introspection/postgresIntrospector.d.ts +30 -21
- package/dist/migrate/introspection/postgresIntrospector.js +70 -43
- package/dist/migrate/introspection/sqliteIntrospector.d.ts +1 -0
- package/dist/migrate/introspection/sqliteIntrospector.js +13 -8
- package/dist/migrate/migrator.d.ts +33 -1
- package/dist/migrate/migrator.js +111 -43
- package/dist/migrate/schemaChange.d.ts +18 -0
- package/dist/migrate/schemaChange.js +37 -0
- package/dist/migrate/schemaGenerator.d.ts +25 -15
- package/dist/migrate/schemaGenerator.js +130 -182
- package/dist/migrate/storage/databaseStorage.d.ts +4 -0
- package/dist/migrate/storage/databaseStorage.js +14 -8
- package/dist/migrate/triggerSql.d.ts +24 -0
- package/dist/migrate/triggerSql.js +229 -0
- package/dist/mongo/mongoDialect.d.ts +0 -21
- package/dist/mongo/mongoDialect.js +105 -100
- package/dist/mongo/mongodbQuerier.js +17 -1
- package/dist/mssql/mssqlDialect.d.ts +18 -7
- package/dist/mssql/mssqlDialect.js +77 -33
- package/dist/mssql/mssqlQuerier.js +2 -2
- package/dist/schema/canonicalType.d.ts +6 -1
- package/dist/schema/canonicalType.js +14 -0
- 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 +4 -8
- package/dist/schema/schemaASTBuilder.js +20 -29
- 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/sqlite/sqliteDialect.d.ts +1 -1
- package/dist/sqlite/sqliteDialect.js +12 -3
- package/dist/type/dialect.d.ts +69 -9
- package/dist/type/entity.d.ts +96 -3
- package/dist/type/migration.d.ts +43 -44
- package/dist/type/query.d.ts +13 -4
- package/dist/type/queryWhere.d.ts +4 -2
- package/dist/util/ddlExpression.util.d.ts +5 -1
- package/dist/util/ddlExpression.util.js +6 -2
- package/dist/util/field.util.d.ts +9 -1
- package/dist/util/field.util.js +14 -2
- package/dist/util/fieldOption.util.d.ts +2 -2
- package/dist/util/fieldOption.util.js +2 -2
- package/dist/util/raw.d.ts +9 -1
- package/dist/util/raw.js +36 -11
- package/dist/util/sql.util.d.ts +12 -0
- package/dist/util/sql.util.js +24 -3
- package/dist/util/uqlError.d.ts +2 -0
- package/dist/util/uqlError.js +4 -0
- package/package.json +4 -4
- package/skills/uql-orm/SKILL.md +11 -6
|
@@ -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.
|
|
@@ -36,7 +36,7 @@ export declare class SqliteDialect extends AbstractSqlDialect {
|
|
|
36
36
|
*/
|
|
37
37
|
protected appendDefaultInsertValue(ctx: QueryContext, field: FieldOptions | undefined): void;
|
|
38
38
|
protected readonly caseInsensitiveMatch = "native";
|
|
39
|
-
|
|
39
|
+
neExpr(field: string, ph: string): string;
|
|
40
40
|
normalizeValue(value: unknown): unknown;
|
|
41
41
|
/**
|
|
42
42
|
* `OFFSET` is only legal after a `LIMIT` here too, so a bare `$skip` needs one - `-1` being
|
|
@@ -17,7 +17,6 @@ function ftsQuery(columns, value) {
|
|
|
17
17
|
}
|
|
18
18
|
/** What SQLite and the engines derived from it have. */
|
|
19
19
|
export const SQLITE_FEATURES = {
|
|
20
|
-
ifNotExists: true,
|
|
21
20
|
indexIfNotExists: true,
|
|
22
21
|
schemas: false, // SQLite's namespaces are attached database files, not declared objects
|
|
23
22
|
dropTableCascade: false,
|
|
@@ -41,6 +40,16 @@ export const SQLITE_FEATURES = {
|
|
|
41
40
|
narrowVectorTypes: false,
|
|
42
41
|
vectorTuningNeedsTransaction: false,
|
|
43
42
|
serialDeclaresPrimaryKey: true,
|
|
43
|
+
triggers: {
|
|
44
|
+
preamble: '',
|
|
45
|
+
assignsRow: false,
|
|
46
|
+
body: 'inline',
|
|
47
|
+
guards: 'clause',
|
|
48
|
+
layout: 'timingFirst',
|
|
49
|
+
rows: 'row',
|
|
50
|
+
scope: 'schema',
|
|
51
|
+
before: true,
|
|
52
|
+
},
|
|
44
53
|
};
|
|
45
54
|
export class SqliteDialect extends AbstractSqlDialect {
|
|
46
55
|
features = SQLITE_FEATURES;
|
|
@@ -114,8 +123,8 @@ export class SqliteDialect extends AbstractSqlDialect {
|
|
|
114
123
|
// SQLite's `LIKE` already ignores case on both sides, for ASCII - and only ASCII, with or without
|
|
115
124
|
// `NOCASE`, so folding the pattern here would break the accented text the engine leaves alone.
|
|
116
125
|
caseInsensitiveMatch = 'native';
|
|
117
|
-
|
|
118
|
-
return
|
|
126
|
+
neExpr(field, ph) {
|
|
127
|
+
return `${field} IS NOT ${ph}`;
|
|
119
128
|
}
|
|
120
129
|
normalizeValue(value) {
|
|
121
130
|
if (value instanceof Date)
|
package/dist/type/dialect.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { EntityMeta, UpdatePayload } from './entity.js';
|
|
2
|
-
import type { Query, QueryConflictPaths,
|
|
2
|
+
import type { Query, QueryConflictPaths, QueryPage, QueryRenderOptions, QuerySearch, RelationQuery } from './query.js';
|
|
3
3
|
import type { QueryAggMap, QueryAggregate, QueryAggregateOp, QueryGroupMap } from './queryAggregate.js';
|
|
4
4
|
import type { QueryWhere } from './queryWhere.js';
|
|
5
5
|
import type { Type } from './utility.js';
|
|
@@ -7,7 +7,7 @@ import type { QueryVectorQuery } from './vector.js';
|
|
|
7
7
|
/**
|
|
8
8
|
* comparison options.
|
|
9
9
|
*/
|
|
10
|
-
export type QueryComparisonOptions =
|
|
10
|
+
export type QueryComparisonOptions = QueryRenderOptions & {
|
|
11
11
|
/**
|
|
12
12
|
* Whether this fragment is rendered as an operand of an enclosing `AND`/`OR`/`NOT`. An operand
|
|
13
13
|
* parenthesizes itself when it emits more than one term, so no fragment ever depends on the
|
|
@@ -78,7 +78,6 @@ export type InsertIdSource = 'returning' | 'firstId';
|
|
|
78
78
|
* Features of the database engine (SQL syntax layer).
|
|
79
79
|
*/
|
|
80
80
|
export interface DialectFeatures {
|
|
81
|
-
readonly ifNotExists: boolean;
|
|
82
81
|
readonly indexIfNotExists: boolean;
|
|
83
82
|
/**
|
|
84
83
|
* Whether the engine has namespaces a table can sit behind. `false` leaves every table
|
|
@@ -176,6 +175,61 @@ export interface SqlDialectFeatures extends DialectFeatures {
|
|
|
176
175
|
readonly vectorTuningNeedsTransaction: boolean;
|
|
177
176
|
/** Whether the serial column type states `PRIMARY KEY` itself, as SQLite's `AUTOINCREMENT` must. */
|
|
178
177
|
readonly serialDeclaresPrimaryKey: boolean;
|
|
178
|
+
/** How the engine spells a trigger. One value rather than a flag each, as {@link rowLocks} is. */
|
|
179
|
+
readonly triggers: TriggerFeatures;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* What a trigger body calls the rows it reads. Row-based engines hand it a record on each side; SQL
|
|
183
|
+
* Server hands it the two tables of the set it touched.
|
|
184
|
+
*/
|
|
185
|
+
export type TriggerRowName = 'NEW' | 'OLD' | 'inserted' | 'deleted';
|
|
186
|
+
/** How a dialect spells a trigger, once {@link SqlDialectFeatures.triggers} names its shape. */
|
|
187
|
+
export interface TriggerFeatures {
|
|
188
|
+
/**
|
|
189
|
+
* Where the body lives: a function of its own that the trigger names (the Postgres family), or inside
|
|
190
|
+
* the `CREATE TRIGGER` itself (everywhere else).
|
|
191
|
+
*/
|
|
192
|
+
readonly body: 'function' | 'inline';
|
|
193
|
+
/**
|
|
194
|
+
* How it states which rows it fires for: `UPDATE OF` beside a `WHEN` (`'clause'`), or, where there is
|
|
195
|
+
* no usable `WHEN` - the MySQL family, CockroachDB, SQL Server - the same condition wrapping the body,
|
|
196
|
+
* as `IF c THEN ... END IF;` (`'thenEndIf'`) or T-SQL's `IF c BEGIN ... END` (`'beginEnd'`).
|
|
197
|
+
*/
|
|
198
|
+
readonly guards: 'clause' | 'thenEndIf' | 'beginEnd';
|
|
199
|
+
/**
|
|
200
|
+
* Whether it fires once per row, with a row on each side, or once per statement over the set it
|
|
201
|
+
* touched. SQL Server is the only one here that is set-based, reading `inserted` and `deleted`.
|
|
202
|
+
*/
|
|
203
|
+
readonly rows: 'row' | 'set';
|
|
204
|
+
/**
|
|
205
|
+
* Where a trigger's name is unique, and so what a `DROP` has to name: per table on the Postgres
|
|
206
|
+
* family, which spells `DROP TRIGGER x ON t`, and per schema everywhere else, which spells
|
|
207
|
+
* `DROP TRIGGER x`. Two tables may carry the same trigger name only under `'table'`.
|
|
208
|
+
*/
|
|
209
|
+
readonly scope: 'table' | 'schema';
|
|
210
|
+
/**
|
|
211
|
+
* Where the table sits in the statement: after the timing, `BEFORE UPDATE ON t`, or ahead of it and
|
|
212
|
+
* behind an `AS`, `ON t AFTER UPDATE AS` - which is T-SQL's shape and nobody else's.
|
|
213
|
+
*/
|
|
214
|
+
readonly layout: 'timingFirst' | 'tableFirst';
|
|
215
|
+
/**
|
|
216
|
+
* What every body opens with, or `''`. T-SQL wants `SET NOCOUNT ON`: a trigger running its own DML
|
|
217
|
+
* otherwise sends a rowcount of its own back, and the client reads that as what the original statement
|
|
218
|
+
* affected. Nothing else here needs a preamble.
|
|
219
|
+
*/
|
|
220
|
+
readonly preamble: string;
|
|
221
|
+
/**
|
|
222
|
+
* Whether a body may assign to the row it was handed, `NEW."col" := ...`. SQLite forbids writing `NEW`
|
|
223
|
+
* at all, and SQL Server is handed a set rather than a row, so on both a trigger that fills a column
|
|
224
|
+
* has to restate the row as an `UPDATE` after the write instead.
|
|
225
|
+
*/
|
|
226
|
+
readonly assignsRow: boolean;
|
|
227
|
+
/**
|
|
228
|
+
* Whether it can fire before the write, which is what a stamp needs. SQL Server has only `AFTER` and
|
|
229
|
+
* `INSTEAD OF`, and `INSTEAD OF` would make the trigger responsible for performing the write itself,
|
|
230
|
+
* so a `before*` event is refused there rather than silently made to mean something else.
|
|
231
|
+
*/
|
|
232
|
+
readonly before: boolean;
|
|
179
233
|
}
|
|
180
234
|
/**
|
|
181
235
|
* What a SQL statement is rendered through, as a `raw` callback and a query context see it:
|
|
@@ -186,6 +240,12 @@ export interface SqlQueryDialect {
|
|
|
186
240
|
* The SQL dialect name.
|
|
187
241
|
*/
|
|
188
242
|
readonly dialectName: SqlDialectName;
|
|
243
|
+
/**
|
|
244
|
+
* The engine whose SQL this one also accepts, which is itself unless it is a fork: CockroachDB runs
|
|
245
|
+
* Postgres's PL/pgSQL and MariaDB runs MySQL's. What lets a body, or any other hand-written SQL, be
|
|
246
|
+
* declared once for a family rather than copied per member.
|
|
247
|
+
*/
|
|
248
|
+
readonly dialectFamily: SqlDialectName;
|
|
189
249
|
/**
|
|
190
250
|
* the escape character for identifiers.
|
|
191
251
|
*/
|
|
@@ -193,17 +253,17 @@ export interface SqlQueryDialect {
|
|
|
193
253
|
/** What the engine can do. */
|
|
194
254
|
readonly features: SqlDialectFeatures;
|
|
195
255
|
/** A read; with `totalAlias`, every row also carries the unpaged match count under that alias. */
|
|
196
|
-
find<E>(ctx: QueryContext, entity: Type<E>, q: Query<E>, opts?:
|
|
256
|
+
find<E>(ctx: QueryContext, entity: Type<E>, q: Query<E>, opts?: QueryRenderOptions, totalAlias?: string): void;
|
|
197
257
|
/** A count of the records matching the filter, or of those a page of it takes. */
|
|
198
|
-
count<E>(ctx: QueryContext, entity: Type<E>, q: QueryPage<E>, opts?:
|
|
258
|
+
count<E>(ctx: QueryContext, entity: Type<E>, q: QueryPage<E>, opts?: QueryRenderOptions): void;
|
|
199
259
|
/** An insert of one record or many. */
|
|
200
|
-
insert<E>(ctx: QueryContext, entity: Type<E>, payload: E | E[], opts?:
|
|
260
|
+
insert<E>(ctx: QueryContext, entity: Type<E>, payload: E | E[], opts?: QueryRenderOptions): void;
|
|
201
261
|
/** An update of the records the query matches. */
|
|
202
|
-
update<E>(ctx: QueryContext, entity: Type<E>, q: QuerySearch<E>, payload: UpdatePayload<E>, opts?:
|
|
262
|
+
update<E>(ctx: QueryContext, entity: Type<E>, q: QuerySearch<E>, payload: UpdatePayload<E>, opts?: QueryRenderOptions): void;
|
|
203
263
|
/** An upsert of one record or many by their conflict paths. */
|
|
204
264
|
upsert<E>(ctx: QueryContext, entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: E | E[]): void;
|
|
205
265
|
/** A delete of the records the query matches, a soft delete where the entity has one. */
|
|
206
|
-
delete<E>(ctx: QueryContext, entity: Type<E>, q: QuerySearch<E>, opts?:
|
|
266
|
+
delete<E>(ctx: QueryContext, entity: Type<E>, q: QuerySearch<E>, opts?: QueryRenderOptions): void;
|
|
207
267
|
/**
|
|
208
268
|
* escape an identifier.
|
|
209
269
|
* @param val the value to be escaped
|
|
@@ -237,7 +297,7 @@ export interface SqlQueryDialect {
|
|
|
237
297
|
/**
|
|
238
298
|
* Build an aggregate query.
|
|
239
299
|
*/
|
|
240
|
-
aggregate<E, G extends QueryGroupMap<E>, A extends QueryAggMap<E>>(ctx: QueryContext, entity: Type<E>, q: QueryAggregate<E, G, A>, opts?:
|
|
300
|
+
aggregate<E, G extends QueryGroupMap<E>, A extends QueryAggMap<E>>(ctx: QueryContext, entity: Type<E>, q: QueryAggregate<E, G, A>, opts?: QueryRenderOptions): void;
|
|
241
301
|
/**
|
|
242
302
|
* Get the placeholder for a parameter at the given index (1-based).
|
|
243
303
|
* Default: '?' for MySQL/MariaDB/SQLite, '$n' for PostgreSQL.
|
package/dist/type/entity.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { EnumValues, ForeignKeyAction, IndexType } from '../schema/types.js';
|
|
2
|
+
import type { SqlDialectName } from './dialect.js';
|
|
2
3
|
import type { FilterOptions, RelationQuery } from './query.js';
|
|
3
4
|
import type { ColumnRef, QueryRaw, RelationAggregate } from './queryRaw.js';
|
|
4
5
|
import type { QueryWhere } from './queryWhere.js';
|
|
5
|
-
import type { Except, ExactlyOne, IsEqual, IsMany, Json, Scalar, Type, Unpacked, Writable } from './utility.js';
|
|
6
|
+
import type { AtLeastOne, Except, ExactlyOne, IsEqual, IsMany, Json, Scalar, Type, Unpacked, Writable } from './utility.js';
|
|
6
7
|
import type { VectorDistance, VectorIndexOptions, VectorIndexType } from './vector.js';
|
|
7
8
|
/** Brands the property an entity is identified by, where it is not `id`, `_id` or `uuid`. */
|
|
8
9
|
export declare const idKey: unique symbol;
|
|
@@ -254,8 +255,12 @@ export type FieldOptions<V = TsTypeOf<FieldType>, E = unknown> = {
|
|
|
254
255
|
* subquery a `$count` reads. Both resolve to SQL at registration, so everything downstream sees one.
|
|
255
256
|
*/
|
|
256
257
|
readonly computed?: ComputedSql<E>;
|
|
257
|
-
/**
|
|
258
|
-
|
|
258
|
+
/**
|
|
259
|
+
* Where {@link FieldOptions.computed} lives instead of being spliced into each read. `true` makes it a
|
|
260
|
+
* generated column, which takes only an immutable expression; a list of events makes it a stamp a trigger
|
|
261
|
+
* writes on each, whoever writes the row - how `CURRENT_TIMESTAMP` is kept, where `onUpdate` sees only uql's writes.
|
|
262
|
+
*/
|
|
263
|
+
readonly stored?: boolean | readonly StampEvent[];
|
|
259
264
|
readonly updatable?: boolean;
|
|
260
265
|
readonly eager?: boolean;
|
|
261
266
|
readonly onInsert?: OnFieldCallback<V>;
|
|
@@ -691,11 +696,97 @@ export type EntityMeta<E> = {
|
|
|
691
696
|
checks?: EntityCheckMeta<E>[];
|
|
692
697
|
/** Lifecycle hooks registered via @BeforeInsert, @AfterUpdate, etc. */
|
|
693
698
|
hooks?: Partial<Record<HookEvent, HookRegistration[]>>;
|
|
699
|
+
/** Triggers the database runs, compiled when the schema is built. */
|
|
700
|
+
triggers?: EntityTriggerMeta<E>[];
|
|
694
701
|
/** Bumped by every `define*` call, so what is derived from the metadata can tell it changed. */
|
|
695
702
|
revision: number;
|
|
696
703
|
/** The revision `getMeta` last finalized, which is what makes finalizing idempotent and re-entrant. */
|
|
697
704
|
processedAt?: number;
|
|
698
705
|
};
|
|
706
|
+
/** When the database writes a stamp: as the row is inserted, as it is updated, or both. */
|
|
707
|
+
export type StampEvent = 'insert' | 'update';
|
|
708
|
+
/**
|
|
709
|
+
* The events a trigger fires on: the lifecycle names, minus the upsert pair, which names no event of its
|
|
710
|
+
* own because `ON CONFLICT` fires the insert or the update triggers, and minus `afterLoad`, which is a
|
|
711
|
+
* read. Derived from {@link HookEvent} so the two vocabularies cannot drift.
|
|
712
|
+
*/
|
|
713
|
+
export type TriggerEvent = Exclude<HookEvent, 'beforeUpsert' | 'afterUpsert' | 'afterLoad'>;
|
|
714
|
+
/** Both events for one operation, since a trigger's timing never changes which rows it has. */
|
|
715
|
+
type TriggerEventOn<Op extends string> = Extract<TriggerEvent, `before${Op}` | `after${Op}`>;
|
|
716
|
+
/** The events with a row on both sides, the only ones that can say which columns moved. */
|
|
717
|
+
type TriggerUpdateEvent = TriggerEventOn<'Update'>;
|
|
718
|
+
/**
|
|
719
|
+
* The rows an event has, as `where` keys them: the incoming one on an insert, the outgoing one on a
|
|
720
|
+
* delete, both on an update.
|
|
721
|
+
*/
|
|
722
|
+
type TriggerRow<Ev extends TriggerEvent> = Ev extends TriggerEventOn<'Insert'> ? '$new' : Ev extends TriggerEventOn<'Delete'> ? '$old' : '$new' | '$old';
|
|
723
|
+
/** One row's refs, rendering `NEW."col"` or `OLD."col"`, or `never` where the event has no such row. */
|
|
724
|
+
type TriggerRowRefs<E, Ev extends TriggerEvent, R extends '$new' | '$old'> = R extends TriggerRow<Ev> ? RefMap<E> : never;
|
|
725
|
+
/**
|
|
726
|
+
* What a trigger runs, over its rows: the incoming row first and the outgoing one second, on every event.
|
|
727
|
+
* The one an event lacks is `never`, so reading it does not compile, and a body reading only the outgoing
|
|
728
|
+
* row - `(_newRow, oldRow)` - serves an update and a delete alike.
|
|
729
|
+
*/
|
|
730
|
+
type TriggerBody<E, Ev extends TriggerEvent> = (newRow: TriggerRowRefs<E, Ev, '$new'>, oldRow: TriggerRowRefs<E, Ev, '$old'>) => QueryRaw;
|
|
731
|
+
/**
|
|
732
|
+
* A condition as data: a predicate on each row it names, of the rows the event has, all of which hold.
|
|
733
|
+
* The row is `$`-marked, as the operators inside it are, so it never reads as a field of the entity.
|
|
734
|
+
*/
|
|
735
|
+
type TriggerPredicate<E, Ev extends TriggerEvent> = {
|
|
736
|
+
readonly [R in TriggerRow<Ev>]?: EntityPredicate<E>;
|
|
737
|
+
};
|
|
738
|
+
/**
|
|
739
|
+
* The body, the engine's own SQL: one for every engine it reads alike, or a map naming one per engine
|
|
740
|
+
* where they differ, as SQL Server's set-based `inserted`/`deleted` does. A missing entry for the engine
|
|
741
|
+
* in use is refused at `sync`, since an entity is declared without knowing which pool will render it.
|
|
742
|
+
*/
|
|
743
|
+
type TriggerRun<E, Ev extends TriggerEvent> = TriggerBody<E, Ev> | Readonly<AtLeastOne<Record<SqlDialectName, TriggerBody<E, Ev>>>>;
|
|
744
|
+
/**
|
|
745
|
+
* A trigger, `{ on: 'beforeUpdate', of: (post) => [post.body], run: (newRow) => raw`...` }`.
|
|
746
|
+
*
|
|
747
|
+
* A list rather than a map keyed by the event, as `checks` and `indexes` are lists: several triggers may
|
|
748
|
+
* share an event, they fire in the order written, and each is named, diffed and dropped by that name.
|
|
749
|
+
*/
|
|
750
|
+
export type TriggerOptions<E = unknown> = {
|
|
751
|
+
[Ev in TriggerEvent]: {
|
|
752
|
+
readonly on: Ev;
|
|
753
|
+
/**
|
|
754
|
+
* What to call this trigger within the entity, for a clearer identifier than its event and position.
|
|
755
|
+
* A label, not the identifier: uql prefixes and qualifies what it installs, so it can tell its own
|
|
756
|
+
* objects from hand-written ones and two entities may share a label.
|
|
757
|
+
*/
|
|
758
|
+
readonly name?: string;
|
|
759
|
+
/**
|
|
760
|
+
* The columns whose change the trigger waits for, reading as the `UPDATE OF` it renders. Beside it
|
|
761
|
+
* goes a `WHEN` comparing each with `IS DISTINCT FROM`, which is the point of the pair: `UPDATE OF`
|
|
762
|
+
* fires on a column that was merely assigned, and the comparison narrows that to one that moved.
|
|
763
|
+
*/
|
|
764
|
+
readonly of?: Ev extends TriggerUpdateEvent ? (refs: RefMap<E>) => readonly ColumnRef<string>[] : never;
|
|
765
|
+
/**
|
|
766
|
+
* A further condition, as the `WHEN` the engine evaluates before entering the body, over the rows the
|
|
767
|
+
* body reads: a predicate on each, `{ $old: { status: 'draft' }, $new: { status: 'published' } }`,
|
|
768
|
+
* rendered on every engine from the one declaration, or SQL off them for what no predicate states.
|
|
769
|
+
*/
|
|
770
|
+
readonly where?: TriggerPredicate<E, Ev> | TriggerBody<E, Ev>;
|
|
771
|
+
readonly run: TriggerRun<E, Ev>;
|
|
772
|
+
};
|
|
773
|
+
}[TriggerEvent];
|
|
774
|
+
/**
|
|
775
|
+
* A trigger as entity metadata keeps it: the column callback resolved to keys, and a body widened to
|
|
776
|
+
* take both rows, which the renderer passes whatever the event, each body reading only its own.
|
|
777
|
+
*/
|
|
778
|
+
export type EntityTriggerMeta<E = object> = Except<TriggerOptions<E>, 'of' | 'where' | 'run'> & {
|
|
779
|
+
readonly of?: readonly string[];
|
|
780
|
+
readonly where?: TriggerPredicate<E, TriggerUpdateEvent> | TriggerMetaBody<E>;
|
|
781
|
+
readonly run: TriggerMetaBody<E> | Readonly<Partial<Record<SqlDialectName, TriggerMetaBody<E>>>>;
|
|
782
|
+
};
|
|
783
|
+
/**
|
|
784
|
+
* A body as the renderer calls it, with both rows whatever the event. Bivariant, as {@link EntitySql} is,
|
|
785
|
+
* so a body typing the row its event lacks as `never` is still held here.
|
|
786
|
+
*/
|
|
787
|
+
export type TriggerMetaBody<E> = {
|
|
788
|
+
run(newRow: RefMap<E>, oldRow: RefMap<E>): QueryRaw;
|
|
789
|
+
}['run'];
|
|
699
790
|
/**
|
|
700
791
|
* A table's `CHECK`, `{ where: { balance: { $gte: 0 } } }`, or SQL off the refs,
|
|
701
792
|
* `{ where: (wallet) => raw`${wallet.spent} <= ${wallet.balance}` }`.
|
|
@@ -743,6 +834,8 @@ export type EntityOptions<E = unknown> = {
|
|
|
743
834
|
readonly checks?: readonly CheckOptions<E>[];
|
|
744
835
|
/** Each lifecycle event and the methods it runs, read off the key map: `{ beforeInsert: (post) => [post.stamp] }`. */
|
|
745
836
|
readonly hooks?: Partial<Record<HookEvent, (keys: KeyMap<E>) => readonly MethodKey<E>[]>>;
|
|
837
|
+
/** Triggers the database runs, in the order written. See {@link TriggerOptions}. */
|
|
838
|
+
readonly triggers?: readonly TriggerOptions<E>[];
|
|
746
839
|
};
|
|
747
840
|
/**
|
|
748
841
|
* Everything an index carries beyond its columns, as the migration builder's `table.index(...)` takes it,
|
package/dist/type/migration.d.ts
CHANGED
|
@@ -118,14 +118,7 @@ export interface ColumnSchema extends Omit<ColumnNode, 'type' | 'table' | 'refer
|
|
|
118
118
|
export interface TableSchema {
|
|
119
119
|
readonly name: string;
|
|
120
120
|
readonly columns: ColumnSchema[];
|
|
121
|
-
|
|
122
|
-
readonly primaryKey?: string[];
|
|
123
|
-
/**
|
|
124
|
-
* What the engine calls the key's constraint, where it names one at all - Postgres's `Member_pkey`,
|
|
125
|
-
* MySQL's literal `PRIMARY`, nothing on SQLite. Only a `DROP` needs it, and only the name the
|
|
126
|
-
* database actually reported will do: a derived one would name a constraint that is not there.
|
|
127
|
-
*/
|
|
128
|
-
readonly primaryKeyName?: string;
|
|
121
|
+
readonly primaryKey?: PrimaryKeySchema;
|
|
129
122
|
readonly indexes?: IndexSchema[];
|
|
130
123
|
readonly foreignKeys?: ForeignKeySchema[];
|
|
131
124
|
}
|
|
@@ -176,8 +169,26 @@ export interface ForeignKeySchema {
|
|
|
176
169
|
readonly onUpdate?: ForeignKeyAction;
|
|
177
170
|
}
|
|
178
171
|
/**
|
|
179
|
-
*
|
|
172
|
+
* One object's change: added (`to` alone), dropped (`from` alone), or altered (both), each whole so the
|
|
173
|
+
* change is undone by swapping its ends. No engine alters an index, a key or a foreign key in place, so
|
|
174
|
+
* an alter of one is its drop and its add, which safe mode holds back together.
|
|
180
175
|
*/
|
|
176
|
+
export interface Change<T> {
|
|
177
|
+
readonly from?: T;
|
|
178
|
+
readonly to?: T;
|
|
179
|
+
}
|
|
180
|
+
/** A primary key, whichever side it is read from: the entities, the database, or a diff between them. */
|
|
181
|
+
export interface PrimaryKeySchema {
|
|
182
|
+
/** Its columns **in order**, which is what a composite is: `(a, b)` is not `(b, a)`. */
|
|
183
|
+
readonly columns: readonly string[];
|
|
184
|
+
/**
|
|
185
|
+
* What the engine calls its constraint, where it names one at all - Postgres's `Member_pkey`, MySQL's
|
|
186
|
+
* literal `PRIMARY`, nothing on SQLite. Only a `DROP` needs it, and only the name the database
|
|
187
|
+
* reported will do: a derived one would name a constraint that is not there.
|
|
188
|
+
*/
|
|
189
|
+
readonly name?: string;
|
|
190
|
+
}
|
|
191
|
+
/** A table's differences from what its entity declares, or the table to create or drop. */
|
|
181
192
|
export interface SchemaDiff {
|
|
182
193
|
/** Qualified where the table has a schema, since it is also the key the table is found under. */
|
|
183
194
|
readonly tableName: string;
|
|
@@ -187,36 +198,10 @@ export interface SchemaDiff {
|
|
|
187
198
|
*/
|
|
188
199
|
readonly schema?: string;
|
|
189
200
|
readonly type: 'create' | 'alter' | 'drop';
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
readonly primaryKey?: {
|
|
195
|
-
readonly from: string[];
|
|
196
|
-
readonly to: string[];
|
|
197
|
-
readonly fromName?: string;
|
|
198
|
-
};
|
|
199
|
-
readonly columnsToAdd?: ColumnSchema[];
|
|
200
|
-
readonly columnsToAlter?: {
|
|
201
|
-
from: ColumnSchema;
|
|
202
|
-
to: ColumnSchema;
|
|
203
|
-
}[];
|
|
204
|
-
readonly columnsToDrop?: string[];
|
|
205
|
-
readonly indexesToAdd?: IndexSchema[];
|
|
206
|
-
/** Whole rather than by name, so the rollback can create each again. */
|
|
207
|
-
readonly indexesToDrop?: IndexSchema[];
|
|
208
|
-
readonly foreignKeysToAdd?: ForeignKeySchema[];
|
|
209
|
-
/** Dropped under the name the *database* reported, which is the only name a `DROP` can use. */
|
|
210
|
-
readonly foreignKeysToDrop?: string[];
|
|
211
|
-
/**
|
|
212
|
-
* A constraint whose referential actions changed. Its own field rather than a pair of entries in
|
|
213
|
-
* the two above, because no engine alters an action in place: it is a drop and an add that have to
|
|
214
|
-
* travel together, and safe mode has to hold back both or neither.
|
|
215
|
-
*/
|
|
216
|
-
readonly foreignKeysToAlter?: {
|
|
217
|
-
readonly from: ForeignKeySchema;
|
|
218
|
-
readonly to: ForeignKeySchema;
|
|
219
|
-
}[];
|
|
201
|
+
readonly primaryKey?: Change<PrimaryKeySchema>;
|
|
202
|
+
readonly columns?: readonly Change<ColumnSchema>[];
|
|
203
|
+
readonly indexes?: readonly Change<IndexSchema>[];
|
|
204
|
+
readonly foreignKeys?: readonly Change<ForeignKeySchema>[];
|
|
220
205
|
}
|
|
221
206
|
/**
|
|
222
207
|
* What every sync entry point takes: `safe` keeps it additive, `drop` lets it remove a column, and
|
|
@@ -248,10 +233,14 @@ export interface DropSchemaOptions {
|
|
|
248
233
|
readonly ifExists?: boolean;
|
|
249
234
|
readonly cascade?: boolean;
|
|
250
235
|
}
|
|
236
|
+
/** The triggers uql installed on one table, by name, each with the statements that recreate it as it stands. */
|
|
237
|
+
export type InstalledTriggers = ReadonlyMap<string, readonly string[]>;
|
|
251
238
|
/**
|
|
252
239
|
* Interface for generating DDL statements from entity metadata
|
|
253
240
|
*/
|
|
254
241
|
export interface SchemaGenerator {
|
|
242
|
+
/** Whether a column's stored default is the one the entity declares, as the engine reprints it. Absent where columns are not compared. */
|
|
243
|
+
readonly defaultsEqual?: (expected: unknown, actual: unknown) => boolean;
|
|
255
244
|
/** The whole schema for `entities`, tables then the foreign keys between them, which need every entity at once. */
|
|
256
245
|
generateCreateSchema(entities: readonly Type<object>[], options?: CreateSchemaOptions): string[];
|
|
257
246
|
/**
|
|
@@ -263,13 +252,16 @@ export interface SchemaGenerator {
|
|
|
263
252
|
/** Generate DROP TABLE statement. */
|
|
264
253
|
generateDropTable(tableName: string, options?: DropSchemaOptions): string;
|
|
265
254
|
/**
|
|
266
|
-
*
|
|
255
|
+
* What takes the triggers on `entity`'s table from `installed` - each by name, with the statements that
|
|
256
|
+
* recreate it - to what it declares: nothing where the two agree, which is always on MongoDB.
|
|
267
257
|
*/
|
|
258
|
+
generateTriggers(entity: Type<object>, installed?: InstalledTriggers): string[];
|
|
259
|
+
/** The inverse of {@link generateTriggers} from the same `installed`: its triggers dropped, and the ones it dropped restored. */
|
|
260
|
+
generateTriggersDown(entity: Type<object>, installed?: InstalledTriggers): string[];
|
|
261
|
+
/** A `DROP` for each trigger uql owns among `names` on `entity`'s table, whatever the entity declares. */
|
|
262
|
+
generateTriggerDrops(entity: Type<object>, names: readonly string[]): string[];
|
|
263
|
+
/** The statements taking a table through `diff`; its rollback is the diff reversed, see `reverseDiff`. */
|
|
268
264
|
generateAlterTable(diff: SchemaDiff): string[];
|
|
269
|
-
/**
|
|
270
|
-
* Generate rollback (down) statements for ALTER TABLE based on schema diff
|
|
271
|
-
*/
|
|
272
|
-
generateAlterTableDown(diff: SchemaDiff): string[];
|
|
273
265
|
/**
|
|
274
266
|
* Generate CREATE INDEX statement
|
|
275
267
|
*/
|
|
@@ -321,6 +313,13 @@ export interface SchemaGenerator {
|
|
|
321
313
|
* Interface for introspecting the current database schema
|
|
322
314
|
*/
|
|
323
315
|
export interface SchemaIntrospector {
|
|
316
|
+
/**
|
|
317
|
+
* Every trigger uql installed on `table`, by name, each with the statements that recreate it as it
|
|
318
|
+
* stands. The names say which to drop once an entity no longer declares them; the statements are what
|
|
319
|
+
* a rollback puts back, read off the engine rather than recorded anywhere by uql. One table's alone,
|
|
320
|
+
* so reading it never meets a trigger another writer is dropping from some other table.
|
|
321
|
+
*/
|
|
322
|
+
ownedTriggers(table: string): Promise<InstalledTriggers>;
|
|
324
323
|
/**
|
|
325
324
|
* What this introspector can read back about an index, and so all that diffing may compare.
|
|
326
325
|
* Comparing a feature it cannot read reports the same drift forever: the entity side declares it,
|
package/dist/type/query.d.ts
CHANGED
|
@@ -22,13 +22,22 @@ export type QueryOptions = {
|
|
|
22
22
|
* table look alike. The entity's own filters never count as naming one.
|
|
23
23
|
*/
|
|
24
24
|
unfiltered?: boolean;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* What a statement is rendered with, on top of the options its caller passed. Kept apart from
|
|
28
|
+
* {@link QueryOptions} because that one is public - it is the third argument of every querier method -
|
|
29
|
+
* and none of this is a caller's to set: an alias is the dialect's to choose and to spell.
|
|
30
|
+
*/
|
|
31
|
+
export type QueryRenderOptions = QueryOptions & {
|
|
32
|
+
/** The alias columns are read off, escaped by the dialect unless {@link escapedPrefix} spells it. */
|
|
28
33
|
prefix?: string;
|
|
29
34
|
/**
|
|
30
|
-
*
|
|
35
|
+
* The prefix already written out, for the one caller whose row is not an identifier: a trigger reads
|
|
36
|
+
* `NEW."col"`, where `NEW` is a record the engine declares, and quoting it names a table that is not
|
|
37
|
+
* in scope. Defaults to {@link prefix} escaped.
|
|
31
38
|
*/
|
|
39
|
+
escapedPrefix?: string;
|
|
40
|
+
/** Whether to infer the alias where none is given. */
|
|
32
41
|
autoPrefix?: boolean;
|
|
33
42
|
};
|
|
34
43
|
/**
|
|
@@ -223,6 +223,8 @@ export type QueryWhereElemMatch<U, Raw = QueryRaw> = unknown extends U ? {
|
|
|
223
223
|
} : NonNullable<U> extends Scalar ? QueryWhereFieldOperators<NonNullable<U>, Raw> : {
|
|
224
224
|
[K in keyof NonNullable<U>]?: QueryWhereFieldValue<NonNullable<U>[K], Raw>;
|
|
225
225
|
};
|
|
226
|
+
/** Every operator a field condition takes, which is what a key of one is once checked. */
|
|
227
|
+
export type QueryWhereFieldOp = keyof QueryWhereFieldOperatorMap<unknown>;
|
|
226
228
|
/**
|
|
227
229
|
* Simple relational comparison operators. `Pick`'s constraint ties this back to
|
|
228
230
|
* {@link QueryWhereFieldOperatorMap} so a rename there breaks this union at compile time.
|
|
@@ -251,7 +253,7 @@ type QueryArrayOp = keyof Pick<QueryWhereFieldOperatorMap<unknown>, '$all' | '$s
|
|
|
251
253
|
/**
|
|
252
254
|
* Ordering operators: {@link QueryCompareOp} plus `$between`.
|
|
253
255
|
*/
|
|
254
|
-
type QueryOrderedOp = QueryCompareOp | keyof Pick<QueryWhereFieldOperatorMap<unknown>, '$between'>;
|
|
256
|
+
export type QueryOrderedOp = QueryCompareOp | keyof Pick<QueryWhereFieldOperatorMap<unknown>, '$between'>;
|
|
255
257
|
/**
|
|
256
258
|
* Vector-only operators. `Pick`'s constraint ties this back to {@link QueryWhereFieldOperatorMap}
|
|
257
259
|
* so a rename there breaks this union at compile time.
|
|
@@ -261,7 +263,7 @@ type QueryVectorOp = keyof Pick<QueryWhereFieldOperatorMap<unknown>, '$near'>;
|
|
|
261
263
|
* The operators every field takes. A subtraction, so an operator added to the map without being
|
|
262
264
|
* classified above is offered on every field: classify it first.
|
|
263
265
|
*/
|
|
264
|
-
type QueryCommonOp = Exclude<
|
|
266
|
+
type QueryCommonOp = Exclude<QueryWhereFieldOp, QueryStringOp | QueryArrayOp | QueryOrderedOp | QueryVectorOp>;
|
|
265
267
|
/**
|
|
266
268
|
* Operator keys applicable to a field of type `T`. Brackets prevent union distribution so an
|
|
267
269
|
* optional field (`string | undefined`) or a literal union (`'a' | 'b'`) gates as one type.
|