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
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** Each change's end on `side`, where it has one: what a drop half removes (`from`), or an add half creates (`to`). */
|
|
2
|
+
export function sides(changes, side) {
|
|
3
|
+
return (changes ?? []).flatMap((change) => {
|
|
4
|
+
const end = change[side];
|
|
5
|
+
return end === undefined ? [] : [end];
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
/** What the changes add: each `to` with no `from`. */
|
|
9
|
+
export function added(changes = []) {
|
|
10
|
+
return changes.flatMap(({ from, to }) => (from === undefined && to !== undefined ? [to] : []));
|
|
11
|
+
}
|
|
12
|
+
/** What the changes drop: each `from` with no `to`. */
|
|
13
|
+
export function dropped(changes = []) {
|
|
14
|
+
return changes.flatMap(({ from, to }) => (to === undefined && from !== undefined ? [from] : []));
|
|
15
|
+
}
|
|
16
|
+
/** The changes that alter an object in place, which only a column can. */
|
|
17
|
+
export function alterations(changes = []) {
|
|
18
|
+
return changes.flatMap(({ from, to }) => (from === undefined || to === undefined ? [] : [{ from, to }]));
|
|
19
|
+
}
|
|
20
|
+
/** `items`, or nothing where it has none, so an empty change list is left off a diff. */
|
|
21
|
+
export function nonEmpty(items) {
|
|
22
|
+
return items.length ? items : undefined;
|
|
23
|
+
}
|
|
24
|
+
/** `change` undone: an add becomes a drop, a drop an add, and an alter runs the other way. */
|
|
25
|
+
export function swap({ from, to }) {
|
|
26
|
+
return { from: to, to: from };
|
|
27
|
+
}
|
|
28
|
+
/** `diff` undone: every change swapped, which is what a migration's `down` runs. */
|
|
29
|
+
export function reverseDiff(diff) {
|
|
30
|
+
return {
|
|
31
|
+
...diff,
|
|
32
|
+
primaryKey: diff.primaryKey && swap(diff.primaryKey),
|
|
33
|
+
columns: diff.columns?.map(swap),
|
|
34
|
+
indexes: diff.indexes?.map(swap),
|
|
35
|
+
foreignKeys: diff.foreignKeys?.map(swap),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -68,18 +68,19 @@ export declare class SqlSchemaGenerator implements SchemaGenerator {
|
|
|
68
68
|
*/
|
|
69
69
|
private orderedTables;
|
|
70
70
|
generateDropTable(tableName: string, options?: DropSchemaOptions): string;
|
|
71
|
+
/**
|
|
72
|
+
* The statements taking a table through `diff`, in the one order both directions need: whatever holds
|
|
73
|
+
* something down goes before it and comes back after it. A foreign key holds its columns and the key it
|
|
74
|
+
* points at, so it goes first and comes back last; the key holds its columns; and some engines drop an
|
|
75
|
+
* index along with its column, which would leave nothing to name. An alter is its drop, then its add.
|
|
76
|
+
*/
|
|
71
77
|
generateAlterTable(diff: SchemaDiff): string[];
|
|
72
78
|
/** `ADD CONSTRAINT` for each of `foreignKeys`. */
|
|
73
79
|
private addForeignKeyStatements;
|
|
74
|
-
/** `DROP CONSTRAINT` for each of `constraintNames`, the mirror of {@link addForeignKeyStatements}. */
|
|
75
|
-
private dropForeignKeyStatements;
|
|
76
|
-
/** The `ALTER COLUMN` restating each of `columns`. */
|
|
77
|
-
private alterColumnStatements;
|
|
78
80
|
/** An index added to a table that may already have rows: its `CREATE`, then what the engine needs after. */
|
|
79
81
|
private addIndexStatements;
|
|
80
|
-
/**
|
|
81
|
-
private
|
|
82
|
-
generateAlterTableDown(diff: SchemaDiff): string[];
|
|
82
|
+
/** A column added to a table that exists, and its comment where the engine keeps one apart. */
|
|
83
|
+
private addColumnStatements;
|
|
83
84
|
generateCreateIndex(tableName: string, index: IndexSchema, options?: {
|
|
84
85
|
ifNotExists?: boolean;
|
|
85
86
|
}): string;
|
|
@@ -96,8 +97,8 @@ export declare class SqlSchemaGenerator implements SchemaGenerator {
|
|
|
96
97
|
/**
|
|
97
98
|
* The one place a column definition is spelled, so the `ColumnSchema` and `ColumnNode` paths cannot
|
|
98
99
|
* drift. A key column states `NOT NULL` rather than leave it to the key: SQLite lets a key column hold
|
|
99
|
-
* NULL otherwise, and SQL Server adds no key over a nullable column. `UNIQUE
|
|
100
|
-
* enum's `CHECK` comes last, the only place MariaDB takes it.
|
|
100
|
+
* NULL otherwise, and SQL Server adds no key over a nullable column. Never `UNIQUE`: a unique column is
|
|
101
|
+
* a unique index, which the table creates beside it. An enum's `CHECK` comes last, the only place MariaDB takes it.
|
|
101
102
|
*/
|
|
102
103
|
private renderColumn;
|
|
103
104
|
/**
|
|
@@ -124,10 +125,8 @@ export declare class SqlSchemaGenerator implements SchemaGenerator {
|
|
|
124
125
|
protected diffOptions(): DiffOptions;
|
|
125
126
|
/** Spread, not copied field by field, so a field the node gains cannot go missing here. */
|
|
126
127
|
private columnNodeToSchema;
|
|
127
|
-
/**
|
|
128
|
-
|
|
129
|
-
*/
|
|
130
|
-
protected isDefaultValueEqual(current: unknown, desired: unknown): boolean;
|
|
128
|
+
/** Whether a column's stored default is the one the entity declares, as this engine reprints it. */
|
|
129
|
+
readonly defaultsEqual: (desired: unknown, current: unknown) => boolean;
|
|
131
130
|
generateCreateTableFromNode(table: TableNode, options?: {
|
|
132
131
|
ifNotExists?: boolean;
|
|
133
132
|
}): string[];
|
|
@@ -186,4 +185,4 @@ export declare class SqlSchemaGenerator implements SchemaGenerator {
|
|
|
186
185
|
* The entities as an AST, named by `generator`'s resolvers rather than a naming strategy, which would
|
|
187
186
|
* also rename an explicit `@Entity({ name })` and so compare each table under another name.
|
|
188
187
|
*/
|
|
189
|
-
export declare function buildEntityAST(generator: Pick<SchemaGenerator, 'resolveTableAlias' | 'resolveSchema' | 'resolveColumnName' | 'compileDdl' | 'compileIndexPredicate'>, entities: readonly Type<object>[], options?: Pick<BuildSchemaASTOptions, 'defaultForeignKeyAction' | 'textScoreIndexes'>): SchemaAST;
|
|
188
|
+
export declare function buildEntityAST(generator: Pick<SchemaGenerator, 'resolveTableAlias' | 'resolveSchema' | 'resolveColumnName' | 'compileDdl' | 'compileIndexPredicate'>, entities: readonly Type<object>[], options?: Pick<BuildSchemaASTOptions, 'defaultForeignKeyAction' | 'textScoreIndexes' | 'vectorIndexRequiresNotNull'>): SchemaAST;
|
|
@@ -5,13 +5,15 @@ import { buildSchemaAST } from '../schema/schemaASTBuilder.js';
|
|
|
5
5
|
import { diffRelationshipNodes, diffTable } from '../schema/schemaASTDiffer.js';
|
|
6
6
|
import { isAutoIncrement, qualifyName } from '../util/index.js';
|
|
7
7
|
import { derivedCheckName, derivedForeignKeyName, derivedPrimaryKeyName, isOwnedName } from '../util/sql.util.js';
|
|
8
|
-
import {
|
|
8
|
+
import { UqlUsageError } from '../util/uqlError.js';
|
|
9
|
+
import { sameDefault } from './builder/expressions.js';
|
|
9
10
|
import { splitSqlStatements } from './builder/splitSqlStatements.js';
|
|
10
11
|
import { indexDdlFor, tableDdlFor } from './ddl/index.js';
|
|
11
12
|
import { sizedType } from './ddl/tableDdl.js';
|
|
12
13
|
import { columnForeignKey, columnIndex, fullColumnDefinitionToNode, renderIndexDefinition, tableDefinitionToNode, } from './generator/definitionToNode.js';
|
|
13
14
|
import { indexNodeToSchema } from './generator/indexNodeToSchema.js';
|
|
14
15
|
import { assertIndexPredicate } from './indexPredicate.js';
|
|
16
|
+
import { added, alterations, dropped, nonEmpty, sides } from './schemaChange.js';
|
|
15
17
|
import { dropTrigger, renderTrigger, stampTriggers } from './triggerSql.js';
|
|
16
18
|
/**
|
|
17
19
|
* Unified SQL schema generator.
|
|
@@ -78,6 +80,7 @@ export class SqlSchemaGenerator {
|
|
|
78
80
|
return buildEntityAST(this, entities, {
|
|
79
81
|
defaultForeignKeyAction: this.defaultForeignKeyAction,
|
|
80
82
|
textScoreIndexes: this.dialect.features.textScoreIndexes,
|
|
83
|
+
vectorIndexRequiresNotNull: this.features.vectorIndexRequiresNotNull,
|
|
81
84
|
});
|
|
82
85
|
}
|
|
83
86
|
/**
|
|
@@ -173,97 +176,45 @@ export class SqlSchemaGenerator {
|
|
|
173
176
|
const cascade = options.cascade && this.features.dropTableCascade ? ' CASCADE' : '';
|
|
174
177
|
return `DROP TABLE ${ifExists}${this.escapeId(tableName)}${cascade};`;
|
|
175
178
|
}
|
|
179
|
+
/**
|
|
180
|
+
* The statements taking a table through `diff`, in the one order both directions need: whatever holds
|
|
181
|
+
* something down goes before it and comes back after it. A foreign key holds its columns and the key it
|
|
182
|
+
* points at, so it goes first and comes back last; the key holds its columns; and some engines drop an
|
|
183
|
+
* index along with its column, which would leave nothing to name. An alter is its drop, then its add.
|
|
184
|
+
*/
|
|
176
185
|
generateAlterTable(diff) {
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
...(diff.
|
|
187
|
-
...(
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
// drop an index along with its column, which would leave nothing here to name.
|
|
191
|
-
statements.push(...this.dropIndexStatements(diff.tableName, diff.indexesToDrop, diff.schema));
|
|
192
|
-
for (const column of diff.columnsToAdd ?? []) {
|
|
193
|
-
this.assertColumnAddable(diff.tableName, column);
|
|
194
|
-
statements.push(this.tableDdl.addColumn(diff.tableName, this.generateColumnDefinitionFromSchema(column)));
|
|
195
|
-
statements.push(...this.generateColumnCommentStatement(diff.tableName, column, diff.schema));
|
|
196
|
-
}
|
|
197
|
-
statements.push(...this.alterColumnStatements(diff.tableName, (diff.columnsToAlter ?? []).map((it) => it.to)));
|
|
198
|
-
for (const columnName of diff.columnsToDrop ?? []) {
|
|
199
|
-
statements.push(...this.tableDdl.dropColumn(diff.tableName, columnName));
|
|
200
|
-
}
|
|
201
|
-
statements.push(...this.addIndexStatements(diff.tableName, diff.indexesToAdd));
|
|
202
|
-
// Last, so every column it names exists by now.
|
|
203
|
-
if (diff.primaryKey?.to.length) {
|
|
204
|
-
statements.push(this.generateAddPrimaryKeySql(diff.tableName, diff.primaryKey.to));
|
|
205
|
-
}
|
|
206
|
-
// After the columns, for the same reason the key is: a constraint cannot name one that is not
|
|
207
|
-
// there yet. The add half of an alter rides along, its drop having gone out above.
|
|
208
|
-
statements.push(...this.addForeignKeyStatements(diff.tableName, [
|
|
209
|
-
...(diff.foreignKeysToAdd ?? []),
|
|
210
|
-
...(diff.foreignKeysToAlter ?? []).map((it) => it.to),
|
|
211
|
-
]));
|
|
212
|
-
return statements;
|
|
186
|
+
const { tableName, schema, primaryKey } = diff;
|
|
187
|
+
const { columns } = diff;
|
|
188
|
+
return [
|
|
189
|
+
...sides(diff.foreignKeys, 'from').map((foreignKey) => this.generateDropForeignKeySql(tableName, constraintNameOf(tableName, foreignKey))),
|
|
190
|
+
...(primaryKey?.from ? [this.generateDropPrimaryKeySql(tableName, primaryKey.from.name)] : []),
|
|
191
|
+
...sides(diff.indexes, 'from').map((index) => this.generateDropIndex(tableName, index.name, schema)),
|
|
192
|
+
...added(columns).flatMap((column) => this.addColumnStatements(tableName, column, schema)),
|
|
193
|
+
...alterations(columns).flatMap(({ from, to }) => this.tableDdl.alterColumn(tableName, to, this.generateColumnDefinitionFromSchema(to), from)),
|
|
194
|
+
...dropped(columns).flatMap((column) => this.tableDdl.dropColumn(tableName, column.name)),
|
|
195
|
+
...this.addIndexStatements(tableName, sides(diff.indexes, 'to')),
|
|
196
|
+
...(primaryKey?.to ? [this.generateAddPrimaryKeySql(tableName, primaryKey.to.columns, primaryKey.to.name)] : []),
|
|
197
|
+
...this.addForeignKeyStatements(tableName, sides(diff.foreignKeys, 'to')),
|
|
198
|
+
];
|
|
213
199
|
}
|
|
214
200
|
/** `ADD CONSTRAINT` for each of `foreignKeys`. */
|
|
215
201
|
addForeignKeyStatements(tableName, foreignKeys) {
|
|
216
202
|
return foreignKeys.map((foreignKey) => this.generateAddForeignKeySql(tableName, foreignKey));
|
|
217
203
|
}
|
|
218
|
-
/** `DROP CONSTRAINT` for each of `constraintNames`, the mirror of {@link addForeignKeyStatements}. */
|
|
219
|
-
dropForeignKeyStatements(tableName, constraintNames) {
|
|
220
|
-
return constraintNames.map((name) => this.generateDropForeignKeySql(tableName, name));
|
|
221
|
-
}
|
|
222
|
-
/** The `ALTER COLUMN` restating each of `columns`. */
|
|
223
|
-
alterColumnStatements(tableName, columns) {
|
|
224
|
-
return columns.flatMap((column) => this.generateAlterColumnStatements(tableName, column, this.generateColumnDefinitionFromSchema(column)));
|
|
225
|
-
}
|
|
226
204
|
/** An index added to a table that may already have rows: its `CREATE`, then what the engine needs after. */
|
|
227
|
-
addIndexStatements(tableName, indexes
|
|
205
|
+
addIndexStatements(tableName, indexes) {
|
|
228
206
|
return indexes.flatMap((index) => [
|
|
229
207
|
this.generateCreateIndex(tableName, index),
|
|
230
208
|
...this.indexDdl.settleStatements(tableName, index),
|
|
231
209
|
]);
|
|
232
210
|
}
|
|
233
|
-
/**
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
// first, and a column it is about to drop is then free of anything naming it.
|
|
241
|
-
statements.push(...this.dropForeignKeyStatements(diff.tableName, [
|
|
242
|
-
...(diff.foreignKeysToAdd ?? []).map((it) => constraintNameOf(diff.tableName, it)),
|
|
243
|
-
...(diff.foreignKeysToAlter ?? []).map((it) => constraintNameOf(diff.tableName, it.to)),
|
|
244
|
-
]));
|
|
245
|
-
// The key next, for the same reason: a column the up added cannot be dropped below while the new
|
|
246
|
-
// key still names it. Restored under the name the database gave it, which is what the table had
|
|
247
|
-
// before, rather than a derived one that was never on it.
|
|
248
|
-
if (diff.primaryKey?.to.length) {
|
|
249
|
-
statements.push(this.generateDropPrimaryKeySql(diff.tableName, derivedPrimaryKeyName(diff.tableName, diff.primaryKey.to)));
|
|
250
|
-
}
|
|
251
|
-
for (const column of diff.columnsToAdd ?? []) {
|
|
252
|
-
statements.push(...this.tableDdl.dropColumn(diff.tableName, column.name));
|
|
253
|
-
}
|
|
254
|
-
statements.push(...this.alterColumnStatements(diff.tableName, (diff.columnsToAlter ?? []).map((it) => it.from)));
|
|
255
|
-
statements.push(...this.dropIndexStatements(diff.tableName, diff.indexesToAdd, diff.schema));
|
|
256
|
-
statements.push(...this.addIndexStatements(diff.tableName, diff.indexesToDrop));
|
|
257
|
-
if (diff.primaryKey?.from.length) {
|
|
258
|
-
statements.push(this.generateAddPrimaryKeySql(diff.tableName, diff.primaryKey.from, diff.primaryKey.fromName));
|
|
259
|
-
}
|
|
260
|
-
// The constraint the up replaced, back under the name the database had for it. A foreign key the
|
|
261
|
-
// up *dropped* is not restored: only its name survived the diff, never what it pointed at.
|
|
262
|
-
statements.push(...this.addForeignKeyStatements(diff.tableName, (diff.foreignKeysToAlter ?? []).map((it) => it.from)));
|
|
263
|
-
if (diff.columnsToDrop?.length || diff.foreignKeysToDrop?.length) {
|
|
264
|
-
statements.push(`-- TODO: Manual reversal needed for dropped columns/foreign keys`);
|
|
265
|
-
}
|
|
266
|
-
return statements;
|
|
211
|
+
/** A column added to a table that exists, and its comment where the engine keeps one apart. */
|
|
212
|
+
addColumnStatements(tableName, column, schema) {
|
|
213
|
+
this.assertColumnAddable(tableName, column);
|
|
214
|
+
return [
|
|
215
|
+
this.tableDdl.addColumn(tableName, this.generateColumnDefinitionFromSchema(column)),
|
|
216
|
+
...this.generateColumnCommentStatement(tableName, column, schema),
|
|
217
|
+
];
|
|
267
218
|
}
|
|
268
219
|
generateCreateIndex(tableName, index, options = {}) {
|
|
269
220
|
return this.indexDdl.getCreateIndexStatement(tableName, index, options);
|
|
@@ -288,8 +239,8 @@ export class SqlSchemaGenerator {
|
|
|
288
239
|
/**
|
|
289
240
|
* The one place a column definition is spelled, so the `ColumnSchema` and `ColumnNode` paths cannot
|
|
290
241
|
* drift. A key column states `NOT NULL` rather than leave it to the key: SQLite lets a key column hold
|
|
291
|
-
* NULL otherwise, and SQL Server adds no key over a nullable column. `UNIQUE
|
|
292
|
-
* enum's `CHECK` comes last, the only place MariaDB takes it.
|
|
242
|
+
* NULL otherwise, and SQL Server adds no key over a nullable column. Never `UNIQUE`: a unique column is
|
|
243
|
+
* a unique index, which the table creates beside it. An enum's `CHECK` comes last, the only place MariaDB takes it.
|
|
293
244
|
*/
|
|
294
245
|
renderColumn(column) {
|
|
295
246
|
const type = column.generatedAs
|
|
@@ -299,9 +250,6 @@ export class SqlSchemaGenerator {
|
|
|
299
250
|
if (!column.nullable) {
|
|
300
251
|
def += ' NOT NULL';
|
|
301
252
|
}
|
|
302
|
-
if (column.isUnique && !column.isPrimaryKey) {
|
|
303
|
-
def += ' UNIQUE';
|
|
304
|
-
}
|
|
305
253
|
def += this.tableDdl.defaultClause(column);
|
|
306
254
|
if (column.comment) {
|
|
307
255
|
def += this.generateColumnComment(column.comment);
|
|
@@ -367,72 +315,61 @@ export class SqlSchemaGenerator {
|
|
|
367
315
|
if (!desired) {
|
|
368
316
|
return undefined;
|
|
369
317
|
}
|
|
370
|
-
// Indexes are matched here rather than by the differ, which pairs them by name so that a changed
|
|
371
|
-
// one reads as one index that altered. A migration needs the opposite: an index already in the
|
|
372
|
-
// table, under whatever name, must not be created again, and one whose shape differs is dropped
|
|
373
|
-
// and created anew - no engine alters an index's columns or uniqueness.
|
|
374
318
|
const tableDiff = diffTable(desired, currentTable, { ...this.diffOptions(), compareIndexes: false });
|
|
375
|
-
const indexes = indexChanges(currentTable.name, desired.indexes, currentTable.indexes);
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
319
|
+
const indexes = indexChanges(currentTable.name, desired.indexes, currentTable.indexes, currentTable.indexFacets);
|
|
320
|
+
// An alter's `to` without its values: an alter restates the whole column, and MySQL answers a
|
|
321
|
+
// restated `CHECK` by adding a *second* constraint rather than replacing the first, so the column
|
|
322
|
+
// would accumulate one per alter. An enum's values reach the database with the column and are never
|
|
323
|
+
// restated - which is also why changing them is a hand-written migration. See architecture/roadmap.md.
|
|
324
|
+
const columns = (tableDiff?.columnDiffs ?? []).map((it) => {
|
|
325
|
+
if (it.type === 'add') {
|
|
326
|
+
return { to: this.columnNodeToSchema(it.expected) };
|
|
327
|
+
}
|
|
328
|
+
if (it.type === 'drop') {
|
|
329
|
+
return { from: this.columnNodeToSchema(it.actual) };
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
from: this.columnNodeToSchema(it.actual),
|
|
333
|
+
to: { ...this.columnNodeToSchema(it.expected), enum: undefined },
|
|
334
|
+
};
|
|
335
|
+
});
|
|
336
|
+
const keyDiff = tableDiff?.primaryKeyDiff;
|
|
337
|
+
// The key added named as this generator names it, so the rollback can drop it by that name.
|
|
338
|
+
const primaryKey = keyDiff && {
|
|
339
|
+
from: keyDiff.actual,
|
|
340
|
+
to: keyDiff.expected && {
|
|
341
|
+
columns: keyDiff.expected.columns,
|
|
342
|
+
name: derivedPrimaryKeyName(tableName, keyDiff.expected.columns),
|
|
343
|
+
},
|
|
397
344
|
};
|
|
398
345
|
// This table's own foreign keys. None where the engine cannot alter one (SQLite, short of rebuilding
|
|
399
346
|
// the table), since a difference nothing can apply would throw on every sync; `drift:check` names it.
|
|
400
347
|
const relationDiffs = this.features.foreignKeyAlter
|
|
401
348
|
? diffRelationshipNodes(desired.outgoingRelations, currentTable.outgoingRelations, this.diffOptions())
|
|
402
349
|
: [];
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
!columnsToDrop.length &&
|
|
409
|
-
!indexesToAdd.length &&
|
|
410
|
-
!indexesToDrop.length &&
|
|
411
|
-
!foreignKeysToAdd.length &&
|
|
412
|
-
!foreignKeysToDrop.length &&
|
|
413
|
-
!foreignKeysToAlter.length &&
|
|
414
|
-
!primaryKey) {
|
|
415
|
-
return undefined;
|
|
416
|
-
}
|
|
417
|
-
return {
|
|
350
|
+
const foreignKeys = relationDiffs.map(({ actual, expected }) => ({
|
|
351
|
+
from: actual && foreignKeyOf(actual),
|
|
352
|
+
to: expected && foreignKeyOf(expected),
|
|
353
|
+
}));
|
|
354
|
+
const alter = {
|
|
418
355
|
tableName,
|
|
419
356
|
schema,
|
|
420
357
|
type: 'alter',
|
|
421
358
|
primaryKey,
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
foreignKeysToAlter: foreignKeysToAlter.length ? foreignKeysToAlter : undefined,
|
|
359
|
+
columns: nonEmpty(columns),
|
|
360
|
+
indexes: nonEmpty([
|
|
361
|
+
...indexes.toAdd.map((to) => ({ to: indexNodeToSchema(to) })),
|
|
362
|
+
...indexes.toDrop.map((from) => ({ from: indexNodeToSchema(from) })),
|
|
363
|
+
...indexes.toAlter.map(({ from, to }) => ({ from: indexNodeToSchema(from), to: indexNodeToSchema(to) })),
|
|
364
|
+
]),
|
|
365
|
+
foreignKeys: nonEmpty(foreignKeys),
|
|
430
366
|
};
|
|
367
|
+
return alter.primaryKey || alter.columns || alter.indexes || alter.foreignKeys ? alter : undefined;
|
|
431
368
|
}
|
|
432
369
|
diffOptions() {
|
|
433
370
|
return {
|
|
434
371
|
normalizeType: engineType(this.dialect),
|
|
435
|
-
defaultsEqual:
|
|
372
|
+
defaultsEqual: this.defaultsEqual,
|
|
436
373
|
};
|
|
437
374
|
}
|
|
438
375
|
/** Spread, not copied field by field, so a field the node gains cannot go missing here. */
|
|
@@ -440,58 +377,28 @@ export class SqlSchemaGenerator {
|
|
|
440
377
|
const { table: _table, referencedBy: _referencedBy, references: _references, ...column } = col;
|
|
441
378
|
return { ...column, type: this.columnSqlType(col) };
|
|
442
379
|
}
|
|
443
|
-
/**
|
|
444
|
-
|
|
445
|
-
*/
|
|
446
|
-
isDefaultValueEqual(current, desired) {
|
|
447
|
-
if (current === desired)
|
|
448
|
-
return true;
|
|
449
|
-
// Both spellings of "no default" are the same fact, and engines disagree on which they report:
|
|
450
|
-
// MariaDB says `null` where MySQL says nothing at all. Reading them as different values asked to
|
|
451
|
-
// `MODIFY` every nullable column, on every sync, forever.
|
|
452
|
-
if (current == null || desired == null)
|
|
453
|
-
return current == null && desired == null;
|
|
454
|
-
const normalize = (value) => {
|
|
455
|
-
// Render first: the desired side may be a symbolic expression, the current side is always the
|
|
456
|
-
// engine's own text, and `{"kind":"now"}` matches no spelling of `CURRENT_TIMESTAMP`.
|
|
457
|
-
const val = SqlExpression.isExpression(value) ? formatDefaultValue(value, this.dialect) : value;
|
|
458
|
-
if (typeof val === 'string') {
|
|
459
|
-
let s = val.replace(/::[a-z_]+(\s+[a-z_]+)*(\[\])?$/i, '');
|
|
460
|
-
s = s.replace(/^'(.*)'$/, '$1');
|
|
461
|
-
if (s.toLowerCase() === 'null')
|
|
462
|
-
return 'null';
|
|
463
|
-
return s;
|
|
464
|
-
}
|
|
465
|
-
return typeof val === 'object' ? JSON.stringify(val) : String(val);
|
|
466
|
-
};
|
|
467
|
-
return normalize(current) === normalize(desired);
|
|
468
|
-
}
|
|
380
|
+
/** Whether a column's stored default is the one the entity declares, as this engine reprints it. */
|
|
381
|
+
defaultsEqual = (desired, current) => sameDefault(desired, current, this.dialect);
|
|
469
382
|
generateCreateTableFromNode(table, options = {}) {
|
|
470
383
|
const columns = [];
|
|
471
384
|
const constraints = [];
|
|
472
|
-
// MariaDB rejects a `VECTOR INDEX` whose column is nullable ("All parts of a VECTOR index must
|
|
473
|
-
// be NOT NULL"), so being indexed decides it rather than the entity's own nullability.
|
|
474
|
-
const indexedVectorColumns = new Set(this.features.vectorIndexRequiresNotNull
|
|
475
|
-
? table.indexes.filter((index) => index.type === 'vector').flatMap((idx) => idx.entries.map((e) => e.column))
|
|
476
|
-
: []);
|
|
477
385
|
for (const col of table.columns.values()) {
|
|
478
|
-
|
|
479
|
-
columns.push(colDef);
|
|
386
|
+
columns.push(this.generateColumnFromNode(col));
|
|
480
387
|
}
|
|
481
388
|
// Every key, of any width, as one named constraint beside the checks and foreign keys - so a
|
|
482
389
|
// later `DROP` has something to name. The exception is a dialect whose serial type states the key
|
|
483
390
|
// itself (SQLite's `INTEGER PRIMARY KEY AUTOINCREMENT`, which cannot be split): there the column
|
|
484
391
|
// has already declared it, and saying it again is a second primary key.
|
|
392
|
+
const key = table.primaryKey;
|
|
485
393
|
const declaredByColumn = this.dialect.features.serialDeclaresPrimaryKey &&
|
|
486
|
-
|
|
487
|
-
table.
|
|
488
|
-
if (
|
|
489
|
-
const
|
|
490
|
-
const
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
(table.checks ?? []).forEach((check, i) => {
|
|
394
|
+
key?.columns.length === 1 &&
|
|
395
|
+
table.columns.get(key.columns[0])?.isAutoIncrement;
|
|
396
|
+
if (key && !declaredByColumn) {
|
|
397
|
+
const name = key.name ?? derivedPrimaryKeyName(table.name, key.columns);
|
|
398
|
+
const columns = key.columns.map((column) => this.escapeId(column)).join(', ');
|
|
399
|
+
constraints.push(`CONSTRAINT ${this.escapeId(name)} PRIMARY KEY (${columns})`);
|
|
400
|
+
}
|
|
401
|
+
table.checks.forEach((check, i) => {
|
|
495
402
|
const name = check.name ?? derivedCheckName(table.name, i + 1);
|
|
496
403
|
constraints.push(`CONSTRAINT ${this.escapeId(name)} CHECK (${check.expression})`);
|
|
497
404
|
});
|
|
@@ -630,7 +537,7 @@ export class SqlSchemaGenerator {
|
|
|
630
537
|
}
|
|
631
538
|
generateAddForeignKeySql(tableName, foreignKey) {
|
|
632
539
|
if (!this.features.foreignKeyAlter) {
|
|
633
|
-
throw new
|
|
540
|
+
throw new UqlUsageError(`Dialect ${this.dialect} does not support adding foreign keys to existing tables`);
|
|
634
541
|
}
|
|
635
542
|
const constraint = this.foreignKeyConstraint(tableName, foreignKey, this.escapeId(foreignKey.references.table));
|
|
636
543
|
return `ALTER TABLE ${this.escapeId(tableName)} ADD ${constraint};`;
|
|
@@ -660,7 +567,7 @@ export class SqlSchemaGenerator {
|
|
|
660
567
|
return `ALTER TABLE ${table} DROP PRIMARY KEY;`;
|
|
661
568
|
}
|
|
662
569
|
if (!constraintName) {
|
|
663
|
-
throw new
|
|
570
|
+
throw new UqlUsageError(`Cannot drop the primary key of "${tableName}": ${this.dialect} names the constraint, and ` +
|
|
664
571
|
'introspection did not report a name for it.');
|
|
665
572
|
}
|
|
666
573
|
return `ALTER TABLE ${table} DROP CONSTRAINT ${this.escapeId(constraintName)};`;
|
|
@@ -673,7 +580,7 @@ export class SqlSchemaGenerator {
|
|
|
673
580
|
if (!column.generatedAs || this.features.generatedColumnAdd) {
|
|
674
581
|
return;
|
|
675
582
|
}
|
|
676
|
-
throw new
|
|
583
|
+
throw new UqlUsageError(`${this.dialect}: Cannot add the computed column "${column.name}" to the existing table ` +
|
|
677
584
|
`"${tableName}" - this database only accepts one in a CREATE TABLE. Drop \`stored\` to have the ` +
|
|
678
585
|
'expression spliced into each statement instead, or recreate the table in a written migration.');
|
|
679
586
|
}
|
|
@@ -681,7 +588,7 @@ export class SqlSchemaGenerator {
|
|
|
681
588
|
if (this.features.primaryKeyAlter) {
|
|
682
589
|
return;
|
|
683
590
|
}
|
|
684
|
-
throw new
|
|
591
|
+
throw new UqlUsageError(`${this.dialect}: Cannot change the primary key of "${tableName}" - this database has no ALTER ` +
|
|
685
592
|
'for it. Recreate the table in a written migration.');
|
|
686
593
|
}
|
|
687
594
|
}
|
|
@@ -21,4 +21,4 @@ export declare function dropTrigger<E>(dialect: AbstractSqlDialect, meta: Entity
|
|
|
21
21
|
* column. Generated rather than authored, so unlike an authored body it renders on every engine from
|
|
22
22
|
* one declaration - and not at all on the MySQL family, whose columns stamp themselves.
|
|
23
23
|
*/
|
|
24
|
-
export declare function stampTriggers<E>(dialect: AbstractSqlDialect, meta: EntityMeta<E>): EntityTriggerMeta<E>[];
|
|
24
|
+
export declare function stampTriggers<E extends object>(dialect: AbstractSqlDialect, meta: EntityMeta<E>): EntityTriggerMeta<E>[];
|