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
|
@@ -5,15 +5,15 @@
|
|
|
5
5
|
* actual database schema.
|
|
6
6
|
*/
|
|
7
7
|
import { canonicalToSql, engineType } from '../../schema/canonicalType.js';
|
|
8
|
-
import { diffSchemas, referentialActions } from '../../schema/schemaASTDiffer.js';
|
|
8
|
+
import { defaultsEqualAsWritten, diffSchemas, referentialActions } from '../../schema/schemaASTDiffer.js';
|
|
9
9
|
function resolveOptions(options) {
|
|
10
10
|
return {
|
|
11
11
|
checkTypes: options.checkTypes ?? true,
|
|
12
12
|
checkNullable: options.checkNullable ?? true,
|
|
13
13
|
checkIndexes: options.checkIndexes ?? true,
|
|
14
|
-
indexFacets: options.indexFacets ?? new Set(),
|
|
15
14
|
checkForeignKeys: options.checkForeignKeys ?? true,
|
|
16
|
-
checkDefaults: options.checkDefaults ??
|
|
15
|
+
checkDefaults: options.checkDefaults ?? options.defaultsEqual !== undefined,
|
|
16
|
+
defaultsEqual: options.defaultsEqual ?? defaultsEqualAsWritten,
|
|
17
17
|
excludeTables: options.excludeTables ?? [],
|
|
18
18
|
dialect: options.dialect,
|
|
19
19
|
};
|
|
@@ -27,9 +27,9 @@ export function detectDrift(expectedAST, actualAST, options = {}) {
|
|
|
27
27
|
const { dialect } = opts;
|
|
28
28
|
const diff = diffSchemas(expectedAST, actualAST, {
|
|
29
29
|
compareIndexes: opts.checkIndexes,
|
|
30
|
-
indexFacets: opts.indexFacets,
|
|
31
30
|
compareRelationships: opts.checkForeignKeys,
|
|
32
31
|
excludeTables: opts.excludeTables,
|
|
32
|
+
defaultsEqual: opts.defaultsEqual,
|
|
33
33
|
// Without a dialect there is no engine to compare through, and `formatType` below then reports no
|
|
34
34
|
// type drift at all.
|
|
35
35
|
...(dialect && { normalizeType: engineType(dialect) }),
|
|
@@ -59,10 +59,14 @@ function detectPrimaryKeyDrifts(diff) {
|
|
|
59
59
|
type: 'constraint_mismatch',
|
|
60
60
|
severity: 'critical',
|
|
61
61
|
table: pkDiff.table,
|
|
62
|
-
details: `Primary key of "${pkDiff.table}" is (${pkDiff.actual
|
|
62
|
+
details: `Primary key of "${pkDiff.table}" is (${keyColumns(pkDiff.actual)}) in the database but (${keyColumns(pkDiff.expected)}) in the entity`,
|
|
63
63
|
suggestion: 'Generate a migration to change the primary key',
|
|
64
64
|
}));
|
|
65
65
|
}
|
|
66
|
+
/** A key's columns as a drift names them, `none` where there is no key. */
|
|
67
|
+
function keyColumns(key) {
|
|
68
|
+
return key?.columns.join(', ') || 'none';
|
|
69
|
+
}
|
|
66
70
|
/**
|
|
67
71
|
* Detect table-level drifts (missing/unexpected tables).
|
|
68
72
|
*/
|
|
@@ -121,7 +125,7 @@ function detectColumnDrifts(diff, opts) {
|
|
|
121
125
|
return drifts;
|
|
122
126
|
}
|
|
123
127
|
/**
|
|
124
|
-
* Add drifts for column alterations
|
|
128
|
+
* Add drifts for column alterations: type, nullability and default.
|
|
125
129
|
*/
|
|
126
130
|
function addAlterColumnDrifts(colDiff, drifts, opts) {
|
|
127
131
|
// An auto-increment key is created through the dialect's `serialPrimaryKey`, whose spelling the
|
|
@@ -159,21 +163,17 @@ function addAlterColumnDrifts(colDiff, drifts, opts) {
|
|
|
159
163
|
suggestion: 'Align nullable setting in entity or database',
|
|
160
164
|
});
|
|
161
165
|
}
|
|
162
|
-
if (opts.checkDefaults) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
details: `Default mismatch for "${colDiff.column}"`,
|
|
174
|
-
suggestion: 'Align the default in the entity or the database',
|
|
175
|
-
});
|
|
176
|
-
}
|
|
166
|
+
if (opts.checkDefaults && !opts.defaultsEqual(colDiff.expected.defaultValue, colDiff.actual.defaultValue)) {
|
|
167
|
+
drifts.push({
|
|
168
|
+
type: 'constraint_mismatch',
|
|
169
|
+
severity: 'info',
|
|
170
|
+
table: colDiff.table,
|
|
171
|
+
column: colDiff.column,
|
|
172
|
+
expected: String(colDiff.expected.defaultValue ?? 'NULL'),
|
|
173
|
+
actual: String(colDiff.actual.defaultValue ?? 'NULL'),
|
|
174
|
+
details: `Default mismatch for "${colDiff.column}"`,
|
|
175
|
+
suggestion: 'Align the default in the entity or the database',
|
|
176
|
+
});
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
/**
|
|
@@ -14,7 +14,7 @@ export declare function tableDefinitionToNode(def: TableDefinition, render: (sql
|
|
|
14
14
|
*/
|
|
15
15
|
export declare function fullColumnDefinitionToNode(col: FullColumnDefinition, tableName: string): ColumnNode;
|
|
16
16
|
/**
|
|
17
|
-
* The index a column-level `index` declares, or nothing.
|
|
17
|
+
* The index a column-level `index` or `unique` declares, or nothing: a unique column is a unique index.
|
|
18
18
|
*
|
|
19
19
|
* Shared with `TableBuilder.build`, which lifts these into the table it is creating: written twice,
|
|
20
20
|
* `addColumn` had no lift at all and silently emitted a column with no index.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createTableNode, keyOfColumns } from '../../schema/schemaAST.js';
|
|
1
2
|
import { renderIndexColumn } from '../../util/ddlExpression.util.js';
|
|
2
3
|
import { derivedForeignKeyName, derivedIndexName } from '../../util/sql.util.js';
|
|
3
4
|
/** A table the builder names but has not seen, which the generator reads only the name of. */
|
|
@@ -10,29 +11,17 @@ function unresolvedTable(name) {
|
|
|
10
11
|
* `render`. Free functions and not generator methods: the dialect reaches them only through `render`.
|
|
11
12
|
*/
|
|
12
13
|
export function tableDefinitionToNode(def, render) {
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const table = {
|
|
16
|
-
name: def.name,
|
|
17
|
-
columns,
|
|
18
|
-
primaryKey: [], // placeholder
|
|
19
|
-
indexes: [],
|
|
20
|
-
incomingRelations: [],
|
|
21
|
-
outgoingRelations: [],
|
|
22
|
-
comment: def.comment,
|
|
23
|
-
};
|
|
14
|
+
const table = { ...createTableNode(def.name), comment: def.comment };
|
|
15
|
+
const { columns } = table;
|
|
24
16
|
for (const colDef of def.columns) {
|
|
25
17
|
const node = fullColumnDefinitionToNode(colDef, def.name);
|
|
26
18
|
node.table = table;
|
|
27
19
|
columns.set(node.name, node);
|
|
28
|
-
if (node.isPrimaryKey) {
|
|
29
|
-
pkNodes.push(node);
|
|
30
|
-
}
|
|
31
20
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
:
|
|
35
|
-
|
|
21
|
+
// A declared key keeps only the columns the table has, in its own order.
|
|
22
|
+
table.primaryKey = def.primaryKey
|
|
23
|
+
? { columns: def.primaryKey.filter((name) => columns.has(name)) }
|
|
24
|
+
: keyOfColumns(columns.values());
|
|
36
25
|
for (const idxDef of def.indexes) {
|
|
37
26
|
table.indexes.push({ ...renderIndexDefinition(idxDef, render), table });
|
|
38
27
|
}
|
|
@@ -64,13 +53,13 @@ export function fullColumnDefinitionToNode(col, tableName) {
|
|
|
64
53
|
return { ...column, table: unresolvedTable(tableName), referencedBy: [] };
|
|
65
54
|
}
|
|
66
55
|
/**
|
|
67
|
-
* The index a column-level `index` declares, or nothing.
|
|
56
|
+
* The index a column-level `index` or `unique` declares, or nothing: a unique column is a unique index.
|
|
68
57
|
*
|
|
69
58
|
* Shared with `TableBuilder.build`, which lifts these into the table it is creating: written twice,
|
|
70
59
|
* `addColumn` had no lift at all and silently emitted a column with no index.
|
|
71
60
|
*/
|
|
72
61
|
export function columnIndex(tableName, col) {
|
|
73
|
-
if (!col.index) {
|
|
62
|
+
if (!col.index && !col.isUnique) {
|
|
74
63
|
return undefined;
|
|
75
64
|
}
|
|
76
65
|
return {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { UqlUsageError } from '../../util/uqlError.js';
|
|
1
2
|
export function serializeMongoCommand(command) {
|
|
2
3
|
return JSON.stringify(command);
|
|
3
4
|
}
|
|
@@ -9,7 +10,7 @@ function parseMongoCommand(statement) {
|
|
|
9
10
|
return JSON.parse(statement);
|
|
10
11
|
}
|
|
11
12
|
function unsupportedMongoCommand(statement) {
|
|
12
|
-
return new
|
|
13
|
+
return new UqlUsageError(`unsupported MongoDB migration command: ${statement}`);
|
|
13
14
|
}
|
|
14
15
|
/** Execute one emitted command. */
|
|
15
16
|
export function runMongoCommand(db, statement) {
|
|
@@ -31,8 +31,8 @@ export declare class MongoSchemaGenerator extends MongoDialect implements Schema
|
|
|
31
31
|
ifNotExists?: boolean;
|
|
32
32
|
}): string[];
|
|
33
33
|
generateDropTable(tableName: string): string;
|
|
34
|
+
/** A collection's indexes: each dropped, then each created, an alter as both. */
|
|
34
35
|
generateAlterTable(diff: SchemaDiff): string[];
|
|
35
|
-
generateAlterTableDown(diff: SchemaDiff): string[];
|
|
36
36
|
/** MongoDB has no triggers, and a write to an entity declaring one is refused, so there is none to reconcile. */
|
|
37
37
|
generateTriggers(): string[];
|
|
38
38
|
generateTriggersDown(): string[];
|
|
@@ -6,8 +6,10 @@ import { QueryRaw, } from '../../type/index.js';
|
|
|
6
6
|
import { indexDistance, unsupportedVectorMetric } from '../../type/vector.js';
|
|
7
7
|
import { declaredIndexes, declaredIndexName, renderIndexColumn } from '../../util/ddlExpression.util.js';
|
|
8
8
|
import { fulltextConfig, fulltextWeights } from '../../util/dialect.util.js';
|
|
9
|
+
import { UqlUsageError } from '../../util/uqlError.js';
|
|
9
10
|
import { assertIndexFeatures, assertIndexType } from '../ddl/indexDdl.js';
|
|
10
11
|
import { assertIndexPredicate, refusedIndexPredicate } from '../indexPredicate.js';
|
|
12
|
+
import { sides } from '../schemaChange.js';
|
|
11
13
|
import { renderIndexDefinition } from './definitionToNode.js';
|
|
12
14
|
import { indexNodeToSchema } from './indexNodeToSchema.js';
|
|
13
15
|
import { serializeMongoCommand } from './mongoCommand.js';
|
|
@@ -29,7 +31,7 @@ export class MongoSchemaGenerator extends MongoDialect {
|
|
|
29
31
|
}
|
|
30
32
|
/** A collection has no SQL to render a check, a computed column or an index expression into. */
|
|
31
33
|
compileDdl() {
|
|
32
|
-
throw new
|
|
34
|
+
throw new UqlUsageError('mongodb has no SQL to render a check, a computed column or an index expression into');
|
|
33
35
|
}
|
|
34
36
|
/**
|
|
35
37
|
* A document store has no cross-collection constraint, so unlike the SQL generator there is nothing to
|
|
@@ -83,7 +85,7 @@ export class MongoSchemaGenerator extends MongoDialect {
|
|
|
83
85
|
*/
|
|
84
86
|
compileIndexPredicate(where, entity, indexName) {
|
|
85
87
|
if (where instanceof QueryRaw) {
|
|
86
|
-
throw new
|
|
88
|
+
throw new UqlUsageError(`mongodb does not support partial indexes from a SQL predicate (index "${indexName}")`);
|
|
87
89
|
}
|
|
88
90
|
assertIndexPredicate(where, this.dialectName, indexName);
|
|
89
91
|
const filter = this.renderFilter(entity, where);
|
|
@@ -106,16 +108,11 @@ export class MongoSchemaGenerator extends MongoDialect {
|
|
|
106
108
|
generateDropTable(tableName) {
|
|
107
109
|
return serializeMongoCommand({ action: 'dropCollection', name: tableName });
|
|
108
110
|
}
|
|
111
|
+
/** A collection's indexes: each dropped, then each created, an alter as both. */
|
|
109
112
|
generateAlterTable(diff) {
|
|
110
113
|
return [
|
|
111
|
-
...(diff.
|
|
112
|
-
...(diff.
|
|
113
|
-
];
|
|
114
|
-
}
|
|
115
|
-
generateAlterTableDown(diff) {
|
|
116
|
-
return [
|
|
117
|
-
...(diff.indexesToAdd ?? []).map((index) => this.dropIndexCommand(diff.tableName, index)),
|
|
118
|
-
...(diff.indexesToDrop ?? []).map((index) => this.generateCreateIndex(diff.tableName, index)),
|
|
114
|
+
...sides(diff.indexes, 'from').map((index) => this.dropIndexCommand(diff.tableName, index)),
|
|
115
|
+
...sides(diff.indexes, 'to').map((index) => this.generateCreateIndex(diff.tableName, index)),
|
|
119
116
|
];
|
|
120
117
|
}
|
|
121
118
|
/** MongoDB has no triggers, and a write to an entity declaring one is refused, so there is none to reconcile. */
|
|
@@ -164,7 +161,7 @@ export class MongoSchemaGenerator extends MongoDialect {
|
|
|
164
161
|
assertIndexFeatures(index, new Set(), this.dialectName);
|
|
165
162
|
const [vector, ...filters] = index.entries;
|
|
166
163
|
if (!vector || index.dimensions === undefined) {
|
|
167
|
-
throw new
|
|
164
|
+
throw new UqlUsageError(`an Atlas vector search index states its field's dimensions (index "${index.name}")`);
|
|
168
165
|
}
|
|
169
166
|
const distance = indexDistance(index);
|
|
170
167
|
const similarity = ATLAS_SIMILARITY[distance];
|
|
@@ -196,7 +193,7 @@ export class MongoSchemaGenerator extends MongoDialect {
|
|
|
196
193
|
case 'createTable': {
|
|
197
194
|
const { name, columns, indexes } = operation.table;
|
|
198
195
|
if (columns.length) {
|
|
199
|
-
throw new
|
|
196
|
+
throw new UqlUsageError(`mongodb does not support columns in a migration (collection "${name}")`);
|
|
200
197
|
}
|
|
201
198
|
return [
|
|
202
199
|
serializeMongoCommand({ action: 'createCollection', name }),
|
|
@@ -212,7 +209,7 @@ export class MongoSchemaGenerator extends MongoDialect {
|
|
|
212
209
|
case 'dropIndex':
|
|
213
210
|
return [this.generateDropIndex(operation.tableName, operation.indexName)];
|
|
214
211
|
default:
|
|
215
|
-
throw new
|
|
212
|
+
throw new UqlUsageError(`mongodb does not support ${operation.type} in a migration`);
|
|
216
213
|
}
|
|
217
214
|
}
|
|
218
215
|
diffSchema(entity, currentTable) {
|
|
@@ -221,17 +218,13 @@ export class MongoSchemaGenerator extends MongoDialect {
|
|
|
221
218
|
if (!currentTable) {
|
|
222
219
|
return { tableName: collectionName, type: 'create' };
|
|
223
220
|
}
|
|
224
|
-
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
type: 'alter',
|
|
232
|
-
indexesToAdd: toAdd.length ? toAdd : undefined,
|
|
233
|
-
indexesToDrop: toDrop.length ? toDrop.map(indexNodeToSchema) : undefined,
|
|
234
|
-
};
|
|
221
|
+
const { toAdd, toDrop, toAlter } = indexChanges(collectionName, this.indexesOf(meta, collectionName), currentTable.indexes, currentTable.indexFacets);
|
|
222
|
+
const indexes = [
|
|
223
|
+
...toAdd.map((to) => ({ to })),
|
|
224
|
+
...toDrop.map((from) => ({ from: indexNodeToSchema(from) })),
|
|
225
|
+
...toAlter.map(({ from, to }) => ({ from: indexNodeToSchema(from), to })),
|
|
226
|
+
];
|
|
227
|
+
return indexes.length ? { tableName: collectionName, type: 'alter', indexes } : undefined;
|
|
235
228
|
}
|
|
236
229
|
}
|
|
237
230
|
/** The first value `partialFilterExpression` refuses, `null`, or a migration cannot carry as JSON, such as a `Date`. */
|
package/dist/migrate/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { ColumnSchema, DialectName, ForeignKeySchema, IndexSchema, Migration, MigrationDefinition, MigrationResult, MigrationStorage, MigratorOptions, MongoQuerier, SchemaDiff, SchemaGenerator, SchemaIntrospector, SqlDialectName, SqlQuerier, SqlQueryDialect, SyncOptions, TableSchema, } from '../type/index.js';
|
|
1
|
+
export type { Change, ColumnSchema, DialectName, ForeignKeySchema, IndexSchema, Migration, MigrationDefinition, MigrationResult, MigrationStorage, MigratorOptions, MongoQuerier, PrimaryKeySchema, SchemaDiff, SchemaGenerator, SchemaIntrospector, SqlDialectName, SqlQuerier, SqlQueryDialect, SyncOptions, TableSchema, } from '../type/index.js';
|
|
2
2
|
export { type Config, isSqlQuerier } from '../type/index.js';
|
|
3
3
|
export { acquireQuerierForMigrations } from './acquireQuerierForMigrations.js';
|
|
4
4
|
export { assertCliConfig } from './assertCliConfig.js';
|
|
@@ -10,6 +10,7 @@ export * from './drift/index.js';
|
|
|
10
10
|
export * from './introspection/index.js';
|
|
11
11
|
export { migrationBuilderFor } from './migrationTarget.js';
|
|
12
12
|
export { type BuilderMigrationDefinition, defineBuilderMigration, defineMigration, Migrator } from './migrator.js';
|
|
13
|
+
export { reverseDiff } from './schemaChange.js';
|
|
13
14
|
export { SqlSchemaGenerator } from './schemaGenerator.js';
|
|
14
15
|
export { DatabaseMigrationStorage } from './storage/databaseStorage.js';
|
|
15
16
|
export { JsonMigrationStorage } from './storage/jsonStorage.js';
|
package/dist/migrate/index.js
CHANGED
|
@@ -16,6 +16,7 @@ export * from './introspection/index.js';
|
|
|
16
16
|
export { migrationBuilderFor } from './migrationTarget.js';
|
|
17
17
|
export { defineBuilderMigration, defineMigration, Migrator } from './migrator.js';
|
|
18
18
|
// Schema generators
|
|
19
|
+
export { reverseDiff } from './schemaChange.js';
|
|
19
20
|
export { SqlSchemaGenerator } from './schemaGenerator.js';
|
|
20
21
|
// Storage implementations
|
|
21
22
|
export { DatabaseMigrationStorage } from './storage/databaseStorage.js';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { QueryRaw } from '../type/queryRaw.js';
|
|
2
2
|
import { isOperatorObject } from '../util/object.util.js';
|
|
3
|
+
import { UqlUsageError } from '../util/uqlError.js';
|
|
3
4
|
/**
|
|
4
5
|
* The `$where` a partial index takes, on the engines where that is less than a query's: SQL Server's
|
|
5
6
|
* `CREATE INDEX` `<filter_predicate>` (comparisons and `IN` on plain columns, joined by `AND`), and
|
|
@@ -30,7 +31,7 @@ export function assertIndexPredicate(where, dialectName, indexName) {
|
|
|
30
31
|
}
|
|
31
32
|
/** What refusing any part of a partial index's predicate reports. */
|
|
32
33
|
export function refusedIndexPredicate(dialectName, part, indexName) {
|
|
33
|
-
return new
|
|
34
|
+
return new UqlUsageError(`${dialectName} does not support ${part} in a partial index predicate (index "${indexName}")`);
|
|
34
35
|
}
|
|
35
36
|
/** The first part of `where` outside `grammar`, depth-first; a `raw` clause is left alone. */
|
|
36
37
|
function refusedOperator(where, grammar) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ForeignKeyAction } from '../../schema/types.js';
|
|
2
|
-
import type { ColumnSchema, InstalledTriggers, ForeignKeySchema, IndexSchema, QuerierPool, RawRow, SchemaIntrospector, SqlQuerier, TableSchema } from '../../type/index.js';
|
|
2
|
+
import type { ColumnSchema, InstalledTriggers, ForeignKeySchema, IndexSchema, PrimaryKeySchema, QuerierPool, RawRow, SchemaIntrospector, SqlQuerier, TableSchema } from '../../type/index.js';
|
|
3
3
|
import { BaseSqlIntrospector } from './baseSqlIntrospector.js';
|
|
4
4
|
/**
|
|
5
5
|
* Reads the rows of one statement while introspecting a table.
|
|
@@ -43,7 +43,7 @@ export declare abstract class AbstractSqlSchemaIntrospector extends BaseSqlIntro
|
|
|
43
43
|
* it: the whole schema in one read, since a table whose entity stopped declaring one still has one to
|
|
44
44
|
* drop. Ownership is matched here rather than with `LIKE`, whose `_` wildcard would take in a hand-written one.
|
|
45
45
|
*/
|
|
46
|
-
ownedTriggers(): Promise<
|
|
46
|
+
ownedTriggers(table: string): Promise<InstalledTriggers>;
|
|
47
47
|
tableExists(tableName: string): Promise<boolean>;
|
|
48
48
|
/**
|
|
49
49
|
* Introspection reads, so `withQuerier` rather than `transaction`: the pool owns the release either
|
|
@@ -54,10 +54,7 @@ export declare abstract class AbstractSqlSchemaIntrospector extends BaseSqlIntro
|
|
|
54
54
|
protected getColumns(read: TableRowReader, tableName: string): Promise<ColumnSchema[]>;
|
|
55
55
|
protected getIndexes(read: TableRowReader, tableName: string): Promise<IndexSchema[]>;
|
|
56
56
|
protected getForeignKeys(read: TableRowReader, tableName: string): Promise<ForeignKeySchema[]>;
|
|
57
|
-
protected getPrimaryKey(read: TableRowReader, tableName: string): Promise<
|
|
58
|
-
columns?: string[];
|
|
59
|
-
name?: string;
|
|
60
|
-
}>;
|
|
57
|
+
protected getPrimaryKey(read: TableRowReader, tableName: string): Promise<PrimaryKeySchema | undefined>;
|
|
61
58
|
protected tableExistsParams(tableName: string): unknown[];
|
|
62
59
|
protected getColumnsParams(tableName: string): unknown[];
|
|
63
60
|
protected getIndexesParams(tableName: string): unknown[];
|
|
@@ -90,6 +87,7 @@ export declare abstract class AbstractSqlSchemaIntrospector extends BaseSqlIntro
|
|
|
90
87
|
* `definition`, and where the body lives apart, the `requires` recreated first: what is installed, not
|
|
91
88
|
* what uql wrote, which is exactly what a rollback puts back.
|
|
92
89
|
*/
|
|
90
|
+
/** The triggers on the table its one parameter names: each one's `name`, `definition`, and what it `requires`. */
|
|
93
91
|
protected abstract triggersQuery(): string;
|
|
94
92
|
/**
|
|
95
93
|
* Extract table name from a row returned by getTableNamesQuery.
|
|
@@ -113,7 +111,7 @@ export declare abstract class AbstractSqlSchemaIntrospector extends BaseSqlIntro
|
|
|
113
111
|
protected mapPrimaryKeyResult(results: RawRow[]): string[] | undefined;
|
|
114
112
|
/**
|
|
115
113
|
* What the engine calls the key's constraint, where the query reported one. Only a `DROP` needs it,
|
|
116
|
-
* and only the reported name will do - see {@link
|
|
114
|
+
* and only the reported name will do - see {@link PrimaryKeySchema.name}.
|
|
117
115
|
*/
|
|
118
116
|
protected mapPrimaryKeyName(results: RawRow[]): string | undefined;
|
|
119
117
|
/** Parse default value string to appropriate type. */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { FOREIGN_KEY_ACTIONS } from '../../schema/types.js';
|
|
2
2
|
import { isSqlQuerier } from '../../type/index.js';
|
|
3
3
|
import { isOwnedName } from '../../util/sql.util.js';
|
|
4
|
+
import { UqlUsageError } from '../../util/uqlError.js';
|
|
4
5
|
import { BaseSqlIntrospector } from './baseSqlIntrospector.js';
|
|
5
6
|
/** A SQL introspector: an engine states its catalogue queries (`get*Query`) and how their rows map (`map*Result`). */
|
|
6
7
|
export class AbstractSqlSchemaIntrospector extends BaseSqlIntrospector {
|
|
@@ -38,8 +39,7 @@ export class AbstractSqlSchemaIntrospector extends BaseSqlIntrospector {
|
|
|
38
39
|
return {
|
|
39
40
|
name: tableName,
|
|
40
41
|
columns,
|
|
41
|
-
primaryKey
|
|
42
|
-
primaryKeyName: primaryKey.name,
|
|
42
|
+
primaryKey,
|
|
43
43
|
indexes,
|
|
44
44
|
foreignKeys,
|
|
45
45
|
};
|
|
@@ -56,20 +56,13 @@ export class AbstractSqlSchemaIntrospector extends BaseSqlIntrospector {
|
|
|
56
56
|
* it: the whole schema in one read, since a table whose entity stopped declaring one still has one to
|
|
57
57
|
* drop. Ownership is matched here rather than with `LIKE`, whose `_` wildcard would take in a hand-written one.
|
|
58
58
|
*/
|
|
59
|
-
async ownedTriggers() {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
const table = String(row['table']);
|
|
68
|
-
const statements = [row['requires'], row['definition']].filter(Boolean).map((sql) => String(sql));
|
|
69
|
-
byTable.set(table, (byTable.get(table) ?? new Map()).set(name, statements));
|
|
70
|
-
}
|
|
71
|
-
return byTable;
|
|
72
|
-
});
|
|
59
|
+
async ownedTriggers(table) {
|
|
60
|
+
const rows = await this.withSqlQuerier((querier) => querier.all(this.triggersQuery(), [table]));
|
|
61
|
+
return new Map(rows.flatMap((row) => {
|
|
62
|
+
const name = String(row['name']);
|
|
63
|
+
const statements = [row['requires'], row['definition']].filter(Boolean).map((sql) => String(sql));
|
|
64
|
+
return isOwnedName(name) ? [[name, statements]] : [];
|
|
65
|
+
}));
|
|
73
66
|
}
|
|
74
67
|
async tableExists(tableName) {
|
|
75
68
|
return this.withSqlQuerier((querier) => this.tableExistsInternal(createTableRowReader(querier), tableName));
|
|
@@ -81,7 +74,7 @@ export class AbstractSqlSchemaIntrospector extends BaseSqlIntrospector {
|
|
|
81
74
|
withSqlQuerier(task) {
|
|
82
75
|
return this.pool.withQuerier((querier) => {
|
|
83
76
|
if (!isSqlQuerier(querier)) {
|
|
84
|
-
throw new
|
|
77
|
+
throw new UqlUsageError(`${this.constructor.name} requires a SQL-based querier`);
|
|
85
78
|
}
|
|
86
79
|
return task(querier);
|
|
87
80
|
});
|
|
@@ -104,7 +97,8 @@ export class AbstractSqlSchemaIntrospector extends BaseSqlIntrospector {
|
|
|
104
97
|
}
|
|
105
98
|
async getPrimaryKey(read, tableName) {
|
|
106
99
|
const results = await read(this.getPrimaryKeyQuery(tableName), this.getPrimaryKeyParams(tableName));
|
|
107
|
-
|
|
100
|
+
const columns = this.mapPrimaryKeyResult(results);
|
|
101
|
+
return columns && { columns, name: this.mapPrimaryKeyName(results) };
|
|
108
102
|
}
|
|
109
103
|
tableExistsParams(tableName) {
|
|
110
104
|
return [tableName];
|
|
@@ -166,7 +160,7 @@ export class AbstractSqlSchemaIntrospector extends BaseSqlIntrospector {
|
|
|
166
160
|
}
|
|
167
161
|
/**
|
|
168
162
|
* What the engine calls the key's constraint, where the query reported one. Only a `DROP` needs it,
|
|
169
|
-
* and only the reported name will do - see {@link
|
|
163
|
+
* and only the reported name will do - see {@link PrimaryKeySchema.name}.
|
|
170
164
|
*/
|
|
171
165
|
mapPrimaryKeyName(results) {
|
|
172
166
|
const name = results[0]?.['constraint_name'];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { canonicalColumnType } from '../../schema/canonicalType.js';
|
|
2
|
-
import { createTableNode, SchemaAST } from '../../schema/schemaAST.js';
|
|
2
|
+
import { createTableNode, keyOfColumns, SchemaAST } from '../../schema/schemaAST.js';
|
|
3
3
|
import { escapeSqlId } from '../../util/index.js';
|
|
4
4
|
import { derivedForeignKeyName } from '../../util/sql.util.js';
|
|
5
5
|
/**
|
|
@@ -56,7 +56,7 @@ export class BaseSqlIntrospector {
|
|
|
56
56
|
return ast;
|
|
57
57
|
}
|
|
58
58
|
buildTable(schema) {
|
|
59
|
-
const table = createTableNode(schema.name, this.schema);
|
|
59
|
+
const table = createTableNode(schema.name, this.schema, this.indexFacets);
|
|
60
60
|
const { columns } = table;
|
|
61
61
|
for (const col of schema.columns) {
|
|
62
62
|
// Spread, not field by field: a `ColumnSchema` is a `ColumnNode` minus the graph links, so
|
|
@@ -71,12 +71,10 @@ export class BaseSqlIntrospector {
|
|
|
71
71
|
};
|
|
72
72
|
columns.set(col.name, column);
|
|
73
73
|
}
|
|
74
|
-
// From the ordered
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
table.primaryKey.push(...keyColumns.flatMap((name) => columns.get(name) ?? []));
|
|
79
|
-
table.primaryKeyName = schema.primaryKeyName;
|
|
74
|
+
// From the ordered key the query returned, not from the per-column flags: `(a, b)` is a different
|
|
75
|
+
// key from `(b, a)`, and a flag says only that a column is *in* the key. Falls back to the flags
|
|
76
|
+
// for an introspector that reports no key of its own.
|
|
77
|
+
table.primaryKey = schema.primaryKey ?? keyOfColumns(schema.columns);
|
|
80
78
|
return table;
|
|
81
79
|
}
|
|
82
80
|
buildRelationships(ast, tableNodes, schema, fromTable) {
|
|
@@ -105,16 +103,7 @@ export class BaseSqlIntrospector {
|
|
|
105
103
|
// does not have, and the index if that leaves none, is what the entity side does too.
|
|
106
104
|
const entries = idx.entries.filter((entry) => entry.expression || table.columns.has(entry.column));
|
|
107
105
|
if (entries.length > 0) {
|
|
108
|
-
|
|
109
|
-
name: idx.name,
|
|
110
|
-
table,
|
|
111
|
-
entries,
|
|
112
|
-
unique: idx.unique,
|
|
113
|
-
type: idx.type,
|
|
114
|
-
where: idx.where,
|
|
115
|
-
include: idx.include,
|
|
116
|
-
};
|
|
117
|
-
ast.addIndex(index);
|
|
106
|
+
ast.addIndex({ ...idx, table, entries });
|
|
118
107
|
}
|
|
119
108
|
}
|
|
120
109
|
}
|
|
@@ -11,7 +11,7 @@ export declare class MongoSchemaIntrospector implements SchemaIntrospector {
|
|
|
11
11
|
readonly indexFacets: ReadonlySet<IndexFacet>;
|
|
12
12
|
constructor(pool: QuerierPool);
|
|
13
13
|
/** MongoDB has no triggers, so none is ever installed. */
|
|
14
|
-
ownedTriggers(): Promise<
|
|
14
|
+
ownedTriggers(): Promise<InstalledTriggers>;
|
|
15
15
|
introspect(tables?: readonly string[]): Promise<SchemaAST>;
|
|
16
16
|
getTableSchema(tableName: string): Promise<TableSchema | undefined>;
|
|
17
17
|
/** Collections only, the way a SQL engine lists its base tables: no view, nor the `system.views` behind one. */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { textConfigOf } from '../../mongo/textLanguage.js';
|
|
2
2
|
import { createTableNode, SchemaAST } from '../../schema/schemaAST.js';
|
|
3
3
|
import { isMongoQuerier, } from '../../type/index.js';
|
|
4
|
+
import { UqlUsageError } from '../../util/uqlError.js';
|
|
4
5
|
/** What a server without Atlas Search answers a search index command with. */
|
|
5
6
|
const SEARCH_NOT_ENABLED = 31082;
|
|
6
7
|
/**
|
|
@@ -24,7 +25,7 @@ export class MongoSchemaIntrospector {
|
|
|
24
25
|
for (const name of tableNames) {
|
|
25
26
|
const schema = await this.getTableSchema(name);
|
|
26
27
|
if (schema) {
|
|
27
|
-
ast.addTable(buildTable(schema));
|
|
28
|
+
ast.addTable(buildTable(schema, this.indexFacets));
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
31
|
return ast;
|
|
@@ -80,7 +81,7 @@ export class MongoSchemaIntrospector {
|
|
|
80
81
|
withDb(task) {
|
|
81
82
|
return this.pool.withQuerier((querier) => {
|
|
82
83
|
if (!isMongoQuerier(querier)) {
|
|
83
|
-
throw new
|
|
84
|
+
throw new UqlUsageError('MongoSchemaIntrospector requires a MongoDB querier');
|
|
84
85
|
}
|
|
85
86
|
return task(querier.db);
|
|
86
87
|
});
|
|
@@ -107,8 +108,8 @@ async function hasCollection(db, name) {
|
|
|
107
108
|
return collections.length > 0;
|
|
108
109
|
}
|
|
109
110
|
/** Mongo has no columns to read, so a table's are the fields its indexes name, one node per field. */
|
|
110
|
-
function buildTable({ name, indexes = [] }) {
|
|
111
|
-
const table = createTableNode(name);
|
|
111
|
+
function buildTable({ name, indexes = [] }, indexFacets) {
|
|
112
|
+
const table = createTableNode(name, undefined, indexFacets);
|
|
112
113
|
for (const index of indexes) {
|
|
113
114
|
for (const { column } of index.entries) {
|
|
114
115
|
if (!table.columns.has(column)) {
|
|
@@ -11,10 +11,11 @@ export class MsSqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
11
11
|
defaultSchemaExpr = 'SCHEMA_NAME()';
|
|
12
12
|
triggersQuery() {
|
|
13
13
|
return /*sql*/ `
|
|
14
|
-
SELECT
|
|
14
|
+
SELECT t.name AS name, m.definition AS definition
|
|
15
15
|
FROM sys.triggers t
|
|
16
16
|
JOIN sys.sql_modules m ON m.object_id = t.object_id
|
|
17
17
|
WHERE t.parent_id <> 0 AND OBJECT_SCHEMA_NAME(t.parent_id) = ${this.schemaExpr}
|
|
18
|
+
AND OBJECT_NAME(t.parent_id) = ${this.dialect.placeholder(1)}
|
|
18
19
|
`;
|
|
19
20
|
}
|
|
20
21
|
getTableNamesQuery() {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { IndexFacet } from '../../schema/indexDifferences.js';
|
|
1
2
|
import type { ColumnSchema, ForeignKeySchema, IndexSchema } from '../../type/index.js';
|
|
2
3
|
import { AbstractSqlSchemaIntrospector, type JoinedForeignKeyRow, type TableRowReader } from './abstractSqlSchemaIntrospector.js';
|
|
3
4
|
/**
|
|
@@ -17,11 +18,7 @@ export declare class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospect
|
|
|
17
18
|
protected getForeignKeysQuery(_tableName: string): string;
|
|
18
19
|
protected getPrimaryKeyQuery(_tableName: string): string;
|
|
19
20
|
protected mapColumnsResult(_read: TableRowReader, _tableName: string, results: MysqlColumnRow[]): Promise<ColumnSchema[]>;
|
|
20
|
-
protected mapIndexesResult(_read: TableRowReader, _tableName: string, results:
|
|
21
|
-
index_name: string;
|
|
22
|
-
columns: string;
|
|
23
|
-
is_unique: number;
|
|
24
|
-
}[]): Promise<IndexSchema[]>;
|
|
21
|
+
protected mapIndexesResult(_read: TableRowReader, _tableName: string, results: MysqlIndexRow[]): Promise<IndexSchema[]>;
|
|
25
22
|
protected mapForeignKeysResult(_read: TableRowReader, _tableName: string, results: JoinedForeignKeyRow[]): Promise<ForeignKeySchema[]>;
|
|
26
23
|
/**
|
|
27
24
|
* MariaDB prints a string default as the literal it is (`'it''s'`). MySQL prints one bare, save an
|
|
@@ -52,6 +49,19 @@ type MysqlColumnRow = {
|
|
|
52
49
|
* JSON, got LONGTEXT", flagged as data loss) on a table uql created itself.
|
|
53
50
|
*/
|
|
54
51
|
export declare class MariadbSchemaIntrospector extends MysqlSchemaIntrospector {
|
|
52
|
+
/** Whether an index is MariaDB's vector index, and the distance it was built for. */
|
|
53
|
+
readonly indexFacets: ReadonlySet<IndexFacet>;
|
|
54
|
+
/**
|
|
55
|
+
* A vector index's distance is kept only in the table's own definition, ``VECTOR KEY `ix` (`vec`)
|
|
56
|
+
* `DISTANCE`='cosine'``, and left out there for MariaDB's default, euclidean.
|
|
57
|
+
*/
|
|
58
|
+
protected mapIndexesResult(read: TableRowReader, tableName: string, results: MysqlIndexRow[]): Promise<IndexSchema[]>;
|
|
55
59
|
protected mapColumnsResult(read: TableRowReader, tableName: string, results: MysqlColumnRow[]): Promise<ColumnSchema[]>;
|
|
56
60
|
}
|
|
61
|
+
type MysqlIndexRow = {
|
|
62
|
+
index_name: string;
|
|
63
|
+
columns: string;
|
|
64
|
+
is_unique: number;
|
|
65
|
+
method: string;
|
|
66
|
+
};
|
|
57
67
|
export {};
|