uql-orm 0.63.0 → 0.65.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/bunSql/bunSqlQuerier.d.ts +3 -10
- package/dist/bunSql/bunSqlQuerier.js +2 -16
- package/dist/bunSql/bunSqlQuerierPool.d.ts +0 -2
- package/dist/bunSql/bunSqlQuerierPool.js +14 -16
- package/dist/d1/d1Querier.d.ts +13 -34
- package/dist/d1/d1Querier.js +1 -1
- package/dist/d1/d1QuerierPool.d.ts +3 -3
- package/dist/dialect/abstractDialect.d.ts +4 -9
- package/dist/dialect/abstractDialect.js +4 -5
- package/dist/dialect/abstractSqlDialect.d.ts +2 -2
- package/dist/dialect/index.d.ts +0 -1
- package/dist/dialect/index.js +2 -3
- package/dist/dialect/mysqlLikeSqlDialect.js +0 -3
- package/dist/dialect/pgLikeSqlDialect.d.ts +9 -1
- package/dist/dialect/pgLikeSqlDialect.js +8 -5
- package/dist/dialect/queryContext.d.ts +3 -3
- package/dist/entity/metadata/definition.d.ts +6 -0
- package/dist/entity/metadata/definition.js +109 -76
- package/dist/migrate/builder/expressions.js +1 -15
- package/dist/migrate/builder/migrationBuilder.d.ts +6 -28
- package/dist/migrate/builder/migrationBuilder.js +9 -83
- package/dist/migrate/builder/types.d.ts +14 -24
- package/dist/migrate/cli.d.ts +2 -7
- package/dist/migrate/cli.js +22 -43
- package/dist/migrate/codegen/entityCodeGenerator.d.ts +5 -0
- package/dist/migrate/codegen/entityCodeGenerator.js +29 -7
- package/dist/migrate/codegen/indexDecoratorSource.js +1 -5
- package/dist/migrate/codegen/migrationFile.d.ts +9 -1
- package/dist/migrate/codegen/migrationFile.js +2 -1
- package/dist/migrate/codegen/sourceLiteral.d.ts +2 -0
- package/dist/migrate/codegen/sourceLiteral.js +4 -0
- package/dist/migrate/ddl/indexDdl.d.ts +4 -2
- package/dist/migrate/ddl/indexDdl.js +16 -15
- package/dist/migrate/generator/mongoCommand.d.ts +9 -9
- package/dist/migrate/generator/mongoCommand.js +1 -1
- package/dist/migrate/generator/mongoSchemaGenerator.d.ts +21 -26
- package/dist/migrate/generator/mongoSchemaGenerator.js +103 -80
- package/dist/migrate/index.d.ts +2 -2
- package/dist/migrate/index.js +2 -2
- package/dist/migrate/indexPredicate.d.ts +8 -0
- package/dist/migrate/indexPredicate.js +52 -0
- package/dist/migrate/introspection/registry.d.ts +2 -2
- package/dist/migrate/introspection/registry.js +6 -11
- package/dist/migrate/migrationTarget.d.ts +23 -0
- package/dist/migrate/migrationTarget.js +50 -0
- package/dist/migrate/migrator.d.ts +18 -50
- package/dist/migrate/migrator.js +79 -199
- package/dist/migrate/schemaGenerator.d.ts +11 -14
- package/dist/migrate/schemaGenerator.js +42 -12
- package/dist/mongo/mongoDialect.d.ts +7 -2
- package/dist/mongo/mongoDialect.js +1 -4
- package/dist/mssql/mssqlDialect.js +0 -3
- package/dist/pglite/pgliteQuerier.d.ts +2 -6
- package/dist/pglite/pgliteQuerier.js +2 -9
- package/dist/postgres/index.d.ts +0 -1
- package/dist/postgres/index.js +0 -1
- package/dist/querier/abstractSharedHandleQuerierPool.d.ts +2 -5
- package/dist/querier/abstractSharedHandleQuerierPool.js +2 -5
- package/dist/querier/abstractSqlQuerier.d.ts +2 -3
- package/dist/querier/abstractSqlQuerier.js +8 -4
- package/dist/querier/cursorStream.d.ts +13 -0
- package/dist/{postgres/pgCursorStream.js → querier/cursorStream.js} +4 -13
- package/dist/schema/canonicalType.js +1 -1
- package/dist/schema/schemaASTBuilder.d.ts +2 -0
- package/dist/schema/schemaASTBuilder.js +43 -62
- package/dist/sqlite/abstractSqliteQuerier.d.ts +7 -3
- package/dist/sqlite/abstractSqliteQuerier.js +18 -4
- package/dist/sqlite/hranaQuerier.d.ts +1 -1
- package/dist/sqlite/localSqliteQuerierPool.d.ts +7 -0
- package/dist/sqlite/localSqliteQuerierPool.js +19 -0
- package/dist/sqlite/nodeSqliteQuerierPool.js +2 -3
- package/dist/sqlite/sqliteDialect.js +0 -3
- package/dist/sqlite/sqliteQuerier.d.ts +8 -5
- package/dist/sqlite/sqliteQuerier.js +4 -13
- package/dist/sqlite/sqliteQuerierPool.js +4 -4
- package/dist/turso/tursoSessionQuerier.d.ts +2 -2
- package/dist/turso/tursoSessionQuerier.js +16 -18
- package/dist/type/dialect.d.ts +25 -35
- package/dist/type/entity.d.ts +26 -28
- package/dist/type/migration.d.ts +11 -40
- package/dist/type/migratorDialect.d.ts +0 -9
- package/dist/type/migratorDialect.js +1 -16
- package/dist/type/querierPool.d.ts +0 -4
- package/dist/type/queryRaw.d.ts +2 -2
- package/dist/type/universalQuerier.d.ts +2 -2
- package/dist/util/ddlExpression.util.d.ts +3 -1
- package/dist/util/ddlExpression.util.js +14 -0
- package/dist/util/sqlLiteral.js +5 -3
- package/package.json +1 -3
- package/dist/migrate/schemaGeneratorAsync.d.ts +0 -7
- package/dist/migrate/schemaGeneratorAsync.js +0 -12
- package/dist/postgres/pgCursorStream.d.ts +0 -20
- package/dist/postgres/postgresWireDriverCapabilities.d.ts +0 -21
- package/dist/postgres/postgresWireDriverCapabilities.js +0 -21
- package/dist/sqlite/bunSqliteAdapter.bun.d.ts +0 -27
- package/dist/sqlite/bunSqliteAdapter.bun.js +0 -25
- package/dist/sqlite/nodeSqliteAdapter.d.ts +0 -33
- package/dist/sqlite/nodeSqliteAdapter.js +0 -25
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
import { AbstractDialect } from '../../dialect/abstractDialect.js';
|
|
2
1
|
import { getMeta } from '../../entity/index.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { MongoDialect } from '../../mongo/mongoDialect.js';
|
|
3
|
+
import { QueryRaw, } from '../../type/index.js';
|
|
4
|
+
import { declaredIndexes, indexNameParts, renderIndexColumn } from '../../util/ddlExpression.util.js';
|
|
5
5
|
import { derivedIndexName } from '../../util/sql.util.js';
|
|
6
|
+
import { assertIndexFeatures, assertIndexType } from '../ddl/indexDdl.js';
|
|
7
|
+
import { assertIndexPredicate, refusedIndexPredicate } from '../indexPredicate.js';
|
|
6
8
|
import { renderIndexDefinition } from './definitionToNode.js';
|
|
7
|
-
import { indexNodeToSchema } from './indexNodeToSchema.js';
|
|
8
9
|
import { serializeMongoCommand } from './mongoCommand.js';
|
|
9
|
-
|
|
10
|
+
/** The index types a key spec can say: a plain key, or `'text'`. */
|
|
11
|
+
const MONGO_INDEX_TYPES = new Set(['btree', 'fulltext']);
|
|
12
|
+
/** A key spec's one feature beyond its keys: a partial filter. */
|
|
13
|
+
const MONGO_INDEX_FEATURES = new Set(['partial']);
|
|
14
|
+
export class MongoSchemaGenerator extends MongoDialect {
|
|
10
15
|
defaultForeignKeyAction;
|
|
11
|
-
dialectName = 'mongodb';
|
|
12
|
-
featureDefaults = mongoDialectFeatures;
|
|
13
|
-
insertIdSource = 'returning';
|
|
14
16
|
constructor(namingStrategy, defaultForeignKeyAction) {
|
|
15
17
|
super({ namingStrategy });
|
|
16
18
|
this.defaultForeignKeyAction = defaultForeignKeyAction;
|
|
@@ -38,113 +40,117 @@ export class MongoSchemaGenerator extends AbstractDialect {
|
|
|
38
40
|
return entities.filter((entity) => wanted.has(this.resolveTableName(getMeta(entity))));
|
|
39
41
|
}
|
|
40
42
|
/**
|
|
41
|
-
* The indexes
|
|
42
|
-
*
|
|
43
|
-
* One owner because two paths need it: creating a collection, and working out which of its indexes
|
|
44
|
-
* are missing. Derived twice, they drifted the moment either changed how a name is settled.
|
|
43
|
+
* The indexes an entity declares, as the collection would hold them. One owner because two paths need
|
|
44
|
+
* it: creating a collection, and working out which of its indexes are missing.
|
|
45
45
|
*/
|
|
46
|
-
|
|
47
|
-
return
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
46
|
+
indexesOf(meta, collectionName) {
|
|
47
|
+
return declaredIndexes(meta).map((index) => this.indexSchema(meta, collectionName, index));
|
|
48
|
+
}
|
|
49
|
+
/** One declared index, its members resolved to document paths and its `where` to a filter document. */
|
|
50
|
+
indexSchema(meta, collectionName, index) {
|
|
51
|
+
const entries = index.columns
|
|
52
|
+
.map((entry) => renderIndexColumn(entry, () => this.compileDdl()))
|
|
53
|
+
.map((entry) => ({ ...entry, column: this.columnOf(meta, entry.column) }));
|
|
54
|
+
const name = index.name ?? derivedIndexName(collectionName, indexNameParts(entries));
|
|
55
|
+
return {
|
|
56
|
+
name,
|
|
57
|
+
entries,
|
|
58
|
+
unique: index.unique ?? false,
|
|
59
|
+
type: index.type,
|
|
60
|
+
include: index.include,
|
|
61
|
+
where: index.where && this.compileIndexPredicate(index.where, meta.entity, name),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The JSON of the document `partialFilterExpression` takes, refused where the predicate reaches past
|
|
66
|
+
* what that holds or what a migration carries as JSON.
|
|
67
|
+
*/
|
|
68
|
+
compileIndexPredicate(where, entity, indexName) {
|
|
69
|
+
if (where instanceof QueryRaw) {
|
|
70
|
+
throw new TypeError(`mongodb does not support partial indexes from a SQL predicate (index "${indexName}")`);
|
|
71
|
+
}
|
|
72
|
+
assertIndexPredicate(where, this.dialectName, indexName);
|
|
73
|
+
const filter = this.renderFilter(entity, where);
|
|
74
|
+
const refused = refusedFilterValue(filter);
|
|
75
|
+
if (refused) {
|
|
76
|
+
throw refusedIndexPredicate(this.dialectName, refused, indexName);
|
|
77
|
+
}
|
|
78
|
+
return JSON.stringify(filter);
|
|
61
79
|
}
|
|
62
80
|
generateCreateTable(entity, _options) {
|
|
63
81
|
const meta = getMeta(entity);
|
|
64
82
|
const collectionName = this.resolveTableName(meta);
|
|
65
|
-
const indexes = this.fieldIndexes(meta, collectionName);
|
|
66
83
|
// One `createIndex` command each, mirroring the SQL generator's `[CREATE TABLE, ...CREATE INDEX]`,
|
|
67
84
|
// so the key spec is built here and the migrator only executes it.
|
|
68
85
|
return [
|
|
69
86
|
serializeMongoCommand({ action: 'createCollection', name: collectionName }),
|
|
70
|
-
...
|
|
87
|
+
...this.indexesOf(meta, collectionName).map((index) => this.generateCreateIndex(collectionName, index)),
|
|
71
88
|
];
|
|
72
89
|
}
|
|
73
90
|
generateDropTable(tableName) {
|
|
74
91
|
return serializeMongoCommand({ action: 'dropCollection', name: tableName });
|
|
75
92
|
}
|
|
76
93
|
generateAlterTable(diff) {
|
|
77
|
-
|
|
78
|
-
if (diff.indexesToAdd?.length) {
|
|
79
|
-
for (const index of diff.indexesToAdd) {
|
|
80
|
-
statements.push(this.generateCreateIndex(diff.tableName, index));
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return statements;
|
|
94
|
+
return (diff.indexesToAdd ?? []).map((index) => this.generateCreateIndex(diff.tableName, index));
|
|
84
95
|
}
|
|
85
96
|
generateAlterTableDown(diff) {
|
|
86
|
-
|
|
87
|
-
if (diff.indexesToAdd?.length) {
|
|
88
|
-
for (const index of diff.indexesToAdd) {
|
|
89
|
-
statements.push(this.generateDropIndex(diff.tableName, index.name));
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return statements;
|
|
97
|
+
return (diff.indexesToAdd ?? []).map((index) => this.generateDropIndex(diff.tableName, index.name));
|
|
93
98
|
}
|
|
94
99
|
/**
|
|
95
100
|
* MongoDB's key spec is where its index options live: `-1` for a descending entry and `'text'` for a
|
|
96
101
|
* full-text index, which is what `$text` needs since a text index declares its own fields.
|
|
97
102
|
*
|
|
98
|
-
* @remarks The SQL-only
|
|
99
|
-
*
|
|
100
|
-
*
|
|
103
|
+
* @remarks The SQL-only options are refused by the checks the SQL dialects refuse each other's with - a
|
|
104
|
+
* silently weaker index is worse than a clear failure. `where` is the filter's JSON, as
|
|
105
|
+
* {@link compileIndexPredicate} writes it.
|
|
101
106
|
*/
|
|
102
107
|
generateCreateIndex(tableName, index) {
|
|
108
|
+
assertIndexType(index, MONGO_INDEX_TYPES, this.dialectName);
|
|
109
|
+
assertIndexFeatures(index, MONGO_INDEX_FEATURES, this.dialectName);
|
|
103
110
|
const key = {};
|
|
104
111
|
for (const entry of index.entries) {
|
|
105
|
-
if (entry.expression || entry.jsonArray || entry.length !== undefined || entry.nulls || entry.opsClass) {
|
|
106
|
-
throw new TypeError(`mongodb does not support that index column option (index "${index.name}")`);
|
|
107
|
-
}
|
|
108
112
|
key[entry.column] = index.type === 'fulltext' ? 'text' : entry.order === 'desc' ? -1 : 1;
|
|
109
113
|
}
|
|
110
|
-
if (index.where) {
|
|
111
|
-
throw new TypeError(`mongodb does not support partial indexes from a SQL predicate (index "${index.name}")`);
|
|
112
|
-
}
|
|
113
114
|
return serializeMongoCommand({
|
|
114
115
|
action: 'createIndex',
|
|
115
116
|
collection: tableName,
|
|
116
117
|
name: index.name,
|
|
117
118
|
key,
|
|
118
|
-
options: {
|
|
119
|
+
options: {
|
|
120
|
+
unique: index.unique,
|
|
121
|
+
name: index.name,
|
|
122
|
+
partialFilterExpression: index.where && JSON.parse(index.where),
|
|
123
|
+
},
|
|
119
124
|
});
|
|
120
125
|
}
|
|
121
126
|
generateDropIndex(tableName, indexName) {
|
|
122
|
-
return serializeMongoCommand({
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
127
|
+
return serializeMongoCommand({ action: 'dropIndex', collection: tableName, name: indexName });
|
|
128
|
+
}
|
|
129
|
+
/** A collection and its indexes, which is all a document store has: a column, a constraint or SQL throws. */
|
|
130
|
+
generateOperation(operation) {
|
|
131
|
+
const render = (index) => renderIndexDefinition(index, () => this.compileDdl());
|
|
132
|
+
switch (operation.type) {
|
|
133
|
+
case 'createTable': {
|
|
134
|
+
const { name, columns, indexes } = operation.table;
|
|
135
|
+
if (columns.length) {
|
|
136
|
+
throw new TypeError(`mongodb does not support columns in a migration (collection "${name}")`);
|
|
137
|
+
}
|
|
138
|
+
return [
|
|
139
|
+
serializeMongoCommand({ action: 'createCollection', name }),
|
|
140
|
+
...indexes.map((index) => this.generateCreateIndex(name, render(index))),
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
case 'dropTable':
|
|
144
|
+
return [this.generateDropTable(operation.tableName)];
|
|
145
|
+
case 'renameTable':
|
|
146
|
+
return [serializeMongoCommand({ action: 'renameCollection', from: operation.oldName, to: operation.newName })];
|
|
147
|
+
case 'createIndex':
|
|
148
|
+
return [this.generateCreateIndex(operation.tableName, render(operation.index))];
|
|
149
|
+
case 'dropIndex':
|
|
150
|
+
return [this.generateDropIndex(operation.tableName, operation.indexName)];
|
|
151
|
+
default:
|
|
152
|
+
throw new TypeError(`mongodb does not support ${operation.type} in a migration`);
|
|
153
|
+
}
|
|
148
154
|
}
|
|
149
155
|
diffSchema(entity, currentTable) {
|
|
150
156
|
const meta = getMeta(entity);
|
|
@@ -153,7 +159,7 @@ export class MongoSchemaGenerator extends AbstractDialect {
|
|
|
153
159
|
return { tableName: collectionName, type: 'create' };
|
|
154
160
|
}
|
|
155
161
|
const existingIndexes = new Set(currentTable.indexes.map((i) => i.name));
|
|
156
|
-
const indexesToAdd = this.
|
|
162
|
+
const indexesToAdd = this.indexesOf(meta, collectionName).filter((index) => !existingIndexes.has(index.name));
|
|
157
163
|
if (indexesToAdd.length === 0) {
|
|
158
164
|
return undefined;
|
|
159
165
|
}
|
|
@@ -164,3 +170,20 @@ export class MongoSchemaGenerator extends AbstractDialect {
|
|
|
164
170
|
};
|
|
165
171
|
}
|
|
166
172
|
}
|
|
173
|
+
/** The first value `partialFilterExpression` refuses, `null`, or a migration cannot carry as JSON, such as a `Date`. */
|
|
174
|
+
function refusedFilterValue(value) {
|
|
175
|
+
if (value === null) {
|
|
176
|
+
return 'null';
|
|
177
|
+
}
|
|
178
|
+
if (Array.isArray(value)) {
|
|
179
|
+
return value.map(refusedFilterValue).find(Boolean);
|
|
180
|
+
}
|
|
181
|
+
if (typeof value !== 'object') {
|
|
182
|
+
return typeof value === 'bigint' ? 'a bigint' : undefined;
|
|
183
|
+
}
|
|
184
|
+
if (Object.getPrototypeOf(value) === Object.prototype) {
|
|
185
|
+
return Object.values(value).map(refusedFilterValue).find(Boolean);
|
|
186
|
+
}
|
|
187
|
+
const typeName = value.constructor.name;
|
|
188
|
+
return `${/^[aeiou]/i.test(typeName) ? 'an' : 'a'} ${typeName}`;
|
|
189
|
+
}
|
package/dist/migrate/index.d.ts
CHANGED
|
@@ -8,9 +8,9 @@ export * from './codegen/index.js';
|
|
|
8
8
|
export * from './ddl/index.js';
|
|
9
9
|
export * from './drift/index.js';
|
|
10
10
|
export * from './introspection/index.js';
|
|
11
|
+
export { migrationBuilderFor } from './migrationTarget.js';
|
|
11
12
|
export { type BuilderMigrationDefinition, defineBuilderMigration, defineMigration, Migrator } from './migrator.js';
|
|
12
|
-
export {
|
|
13
|
-
export { createSchemaGeneratorAsync } from './schemaGeneratorAsync.js';
|
|
13
|
+
export { SqlSchemaGenerator } from './schemaGenerator.js';
|
|
14
14
|
export { DatabaseMigrationStorage } from './storage/databaseStorage.js';
|
|
15
15
|
export { JsonMigrationStorage } from './storage/jsonStorage.js';
|
|
16
16
|
export { MongoMigrationStorage } from './storage/mongoStorage.js';
|
package/dist/migrate/index.js
CHANGED
|
@@ -13,10 +13,10 @@ export * from './drift/index.js';
|
|
|
13
13
|
// Schema introspection
|
|
14
14
|
export * from './introspection/index.js';
|
|
15
15
|
// Main migrator
|
|
16
|
+
export { migrationBuilderFor } from './migrationTarget.js';
|
|
16
17
|
export { defineBuilderMigration, defineMigration, Migrator } from './migrator.js';
|
|
17
18
|
// Schema generators
|
|
18
|
-
export {
|
|
19
|
-
export { createSchemaGeneratorAsync } from './schemaGeneratorAsync.js';
|
|
19
|
+
export { SqlSchemaGenerator } from './schemaGenerator.js';
|
|
20
20
|
// Storage implementations
|
|
21
21
|
export { DatabaseMigrationStorage } from './storage/databaseStorage.js';
|
|
22
22
|
export { JsonMigrationStorage } from './storage/jsonStorage.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DialectName, EntityWhereMeta } from '../type/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Refuses a partial index's predicate reaching past what the engine takes, before it compiles to what
|
|
4
|
+
* only the server would reject, naming the operator as its author wrote it. A `raw` one is left to the server.
|
|
5
|
+
*/
|
|
6
|
+
export declare function assertIndexPredicate<E>(where: EntityWhereMeta<E>, dialectName: DialectName, indexName: string): void;
|
|
7
|
+
/** What refusing any part of a partial index's predicate reports. */
|
|
8
|
+
export declare function refusedIndexPredicate(dialectName: string, part: string, indexName: string): TypeError;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { QueryRaw } from '../type/queryRaw.js';
|
|
2
|
+
import { isOperatorObject } from '../util/object.util.js';
|
|
3
|
+
/**
|
|
4
|
+
* The `$where` a partial index takes, on the engines where that is less than a query's: SQL Server's
|
|
5
|
+
* `CREATE INDEX` `<filter_predicate>` (comparisons and `IN` on plain columns, joined by `AND`), and
|
|
6
|
+
* MongoDB's `partialFilterExpression` (https://www.mongodb.com/docs/manual/core/index-partial/).
|
|
7
|
+
*/
|
|
8
|
+
const PREDICATE_GRAMMARS = {
|
|
9
|
+
mssql: {
|
|
10
|
+
rootOps: new Set(['$and']),
|
|
11
|
+
fieldOps: new Set(['$eq', '$ne', '$gt', '$gte', '$lt', '$lte', '$in', '$isNull', '$isNotNull']),
|
|
12
|
+
jsonPaths: false,
|
|
13
|
+
},
|
|
14
|
+
mongodb: {
|
|
15
|
+
rootOps: new Set(['$and', '$or']),
|
|
16
|
+
fieldOps: new Set(['$eq', '$gt', '$gte', '$lt', '$lte', '$between', '$in']),
|
|
17
|
+
jsonPaths: true,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Refuses a partial index's predicate reaching past what the engine takes, before it compiles to what
|
|
22
|
+
* only the server would reject, naming the operator as its author wrote it. A `raw` one is left to the server.
|
|
23
|
+
*/
|
|
24
|
+
export function assertIndexPredicate(where, dialectName, indexName) {
|
|
25
|
+
const grammar = PREDICATE_GRAMMARS[dialectName];
|
|
26
|
+
const refused = grammar && !(where instanceof QueryRaw) ? refusedOperator(where, grammar) : undefined;
|
|
27
|
+
if (refused) {
|
|
28
|
+
throw refusedIndexPredicate(dialectName, refused, indexName);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** What refusing any part of a partial index's predicate reports. */
|
|
32
|
+
export function refusedIndexPredicate(dialectName, part, indexName) {
|
|
33
|
+
return new TypeError(`${dialectName} does not support ${part} in a partial index predicate (index "${indexName}")`);
|
|
34
|
+
}
|
|
35
|
+
/** The first part of `where` outside `grammar`, depth-first; a `raw` clause is left alone. */
|
|
36
|
+
function refusedOperator(where, grammar) {
|
|
37
|
+
return Object.entries(where)
|
|
38
|
+
.map(([key, value]) => {
|
|
39
|
+
if (key.includes('.') && !grammar.jsonPaths) {
|
|
40
|
+
return 'a JSON path';
|
|
41
|
+
}
|
|
42
|
+
if (!key.startsWith('$')) {
|
|
43
|
+
return isOperatorObject(value) ? Object.keys(value).find((op) => !grammar.fieldOps.has(op)) : undefined;
|
|
44
|
+
}
|
|
45
|
+
if (!grammar.rootOps.has(key)) {
|
|
46
|
+
return key;
|
|
47
|
+
}
|
|
48
|
+
const clauses = Array.isArray(value) ? value.filter((clause) => !(clause instanceof QueryRaw)) : [];
|
|
49
|
+
return clauses.map((clause) => refusedOperator(clause, grammar)).find(Boolean);
|
|
50
|
+
})
|
|
51
|
+
.find(Boolean);
|
|
52
|
+
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { QuerierPool, SchemaIntrospector } from '../../type/index.js';
|
|
2
|
-
/** The introspector for `
|
|
3
|
-
export declare function introspectorFor(
|
|
2
|
+
/** The introspector for the engine `pool` runs on, reading `schema` where the engine has schemas to read. */
|
|
3
|
+
export declare function introspectorFor(pool: QuerierPool, schema?: string): SchemaIntrospector;
|
|
@@ -4,12 +4,9 @@ import { MariadbSchemaIntrospector, MysqlSchemaIntrospector } from './mysqlIntro
|
|
|
4
4
|
import { CockroachSchemaIntrospector, PostgresSchemaIntrospector } from './postgresIntrospector.js';
|
|
5
5
|
import { SqliteSchemaIntrospector } from './sqliteIntrospector.js';
|
|
6
6
|
/**
|
|
7
|
-
* Which introspector each engine gets
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* Every entry is statically imported, so `uql-orm/migrate` still carries all of them; making the
|
|
11
|
-
* table's values dynamic imports would shrink that entry, at the cost of an async
|
|
12
|
-
* `createIntrospector` the constructor cannot await.
|
|
7
|
+
* Which introspector each engine gets; SQLite and MongoDB have no schemas, so they ignore the argument.
|
|
8
|
+
* Statically imported, so `uql-orm/migrate` carries every one: dynamic imports would shrink that entry,
|
|
9
|
+
* but `Migrator` builds its introspector in its constructor, which cannot await.
|
|
13
10
|
*/
|
|
14
11
|
const INTROSPECTORS = {
|
|
15
12
|
postgres: (pool, schema) => new PostgresSchemaIntrospector(pool, schema),
|
|
@@ -17,12 +14,10 @@ const INTROSPECTORS = {
|
|
|
17
14
|
mysql: (pool, schema) => new MysqlSchemaIntrospector(pool, schema),
|
|
18
15
|
mariadb: (pool, schema) => new MariadbSchemaIntrospector(pool, schema),
|
|
19
16
|
mssql: (pool, schema) => new MsSqlSchemaIntrospector(pool, schema),
|
|
20
|
-
// Neither has schemas to read: SQLite attaches database files and MongoDB takes its database from
|
|
21
|
-
// the connection, so both ignore the argument rather than filtering on it.
|
|
22
17
|
sqlite: (pool) => new SqliteSchemaIntrospector(pool),
|
|
23
18
|
mongodb: (pool) => new MongoSchemaIntrospector(pool),
|
|
24
19
|
};
|
|
25
|
-
/** The introspector for `
|
|
26
|
-
export function introspectorFor(
|
|
27
|
-
return INTROSPECTORS[dialectName]
|
|
20
|
+
/** The introspector for the engine `pool` runs on, reading `schema` where the engine has schemas to read. */
|
|
21
|
+
export function introspectorFor(pool, schema) {
|
|
22
|
+
return INTROSPECTORS[pool.dialect.dialectName](pool, schema);
|
|
28
23
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ForeignKeyAction } from '../schema/types.js';
|
|
2
|
+
import { type MigrationStorage, type MigratorDialect, type Querier, type QuerierPool, type SchemaGenerator } from '../type/index.js';
|
|
3
|
+
import { MigrationBuilder } from './builder/migrationBuilder.js';
|
|
4
|
+
import { type MigrationSource } from './codegen/migrationFile.js';
|
|
5
|
+
/** A migration querier as its engine family drives it. */
|
|
6
|
+
export type MigrationSession = {
|
|
7
|
+
readonly querier: Querier;
|
|
8
|
+
/** Runs one statement its generator wrote. */
|
|
9
|
+
run(statement: string): Promise<unknown>;
|
|
10
|
+
/** `work` in one transaction where the engine takes DDL in one; MongoDB creates collections outside any. */
|
|
11
|
+
transaction(work: () => Promise<void>): Promise<void>;
|
|
12
|
+
};
|
|
13
|
+
/** Everything a migrator does differently per engine family, chosen once from its pool. */
|
|
14
|
+
export type MigrationTarget = {
|
|
15
|
+
readonly source: MigrationSource;
|
|
16
|
+
storage(tableName: string | undefined): MigrationStorage;
|
|
17
|
+
/** The dialect's schema generator. Async because MongoDB's loads its optional peer. */
|
|
18
|
+
generator(): Promise<SchemaGenerator>;
|
|
19
|
+
withSession<T>(task: (session: MigrationSession) => Promise<T>): Promise<T>;
|
|
20
|
+
};
|
|
21
|
+
export declare function migrationTargetFor(pool: QuerierPool<Querier, MigratorDialect>, defaultForeignKeyAction?: ForeignKeyAction): MigrationTarget;
|
|
22
|
+
/** A builder running each operation on `querier`, as SQL or as MongoDB driver commands. */
|
|
23
|
+
export declare function migrationBuilderFor(querier: Querier): Promise<MigrationBuilder>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { isMongoQuerier, isSqlQuerier, } from '../type/index.js';
|
|
2
|
+
import { withMongoQuerierForMigrations, withSqlQuerierForMigrations } from './acquireQuerierForMigrations.js';
|
|
3
|
+
import { MigrationBuilder } from './builder/migrationBuilder.js';
|
|
4
|
+
import { migrationSource } from './codegen/migrationFile.js';
|
|
5
|
+
import { runMongoCommand } from './generator/mongoCommand.js';
|
|
6
|
+
import { SqlSchemaGenerator } from './schemaGenerator.js';
|
|
7
|
+
import { DatabaseMigrationStorage } from './storage/databaseStorage.js';
|
|
8
|
+
import { MongoMigrationStorage } from './storage/mongoStorage.js';
|
|
9
|
+
const sqlSession = (querier) => ({
|
|
10
|
+
querier,
|
|
11
|
+
run: (statement) => querier.run(statement),
|
|
12
|
+
transaction: (work) => querier.transaction(work),
|
|
13
|
+
});
|
|
14
|
+
const mongoSession = (querier) => ({
|
|
15
|
+
querier,
|
|
16
|
+
run: (statement) => runMongoCommand(querier.db, statement),
|
|
17
|
+
transaction: (work) => work(),
|
|
18
|
+
});
|
|
19
|
+
/** Imported on use, so the optional `mongodb` peer loads only on MongoDB. */
|
|
20
|
+
async function mongoSchemaGenerator(namingStrategy, defaultForeignKeyAction) {
|
|
21
|
+
const { MongoSchemaGenerator } = await import('./generator/mongoSchemaGenerator.js');
|
|
22
|
+
return new MongoSchemaGenerator(namingStrategy, defaultForeignKeyAction);
|
|
23
|
+
}
|
|
24
|
+
export function migrationTargetFor(pool, defaultForeignKeyAction) {
|
|
25
|
+
const { dialect } = pool;
|
|
26
|
+
if (dialect.dialectName === 'mongodb') {
|
|
27
|
+
return {
|
|
28
|
+
source: migrationSource.MongoQuerier,
|
|
29
|
+
storage: (tableName) => new MongoMigrationStorage(pool, { tableName }),
|
|
30
|
+
generator: () => mongoSchemaGenerator(dialect.namingStrategy, defaultForeignKeyAction),
|
|
31
|
+
withSession: (task) => withMongoQuerierForMigrations(pool, 'Migrator', (querier) => task(mongoSession(querier))),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
source: migrationSource.SqlQuerier,
|
|
36
|
+
storage: (tableName) => new DatabaseMigrationStorage(pool, { tableName }),
|
|
37
|
+
generator: async () => new SqlSchemaGenerator(dialect, defaultForeignKeyAction),
|
|
38
|
+
withSession: (task) => withSqlQuerierForMigrations(pool, 'Migrator', (querier) => task(sqlSession(querier))),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** A builder running each operation on `querier`, as SQL or as MongoDB driver commands. */
|
|
42
|
+
export async function migrationBuilderFor(querier) {
|
|
43
|
+
if (isSqlQuerier(querier)) {
|
|
44
|
+
return new MigrationBuilder(new SqlSchemaGenerator(querier.dialect), sqlSession(querier).run);
|
|
45
|
+
}
|
|
46
|
+
if (isMongoQuerier(querier)) {
|
|
47
|
+
return new MigrationBuilder(await mongoSchemaGenerator(), mongoSession(querier).run);
|
|
48
|
+
}
|
|
49
|
+
throw new TypeError('A migration builder needs a SQL or a MongoDB querier');
|
|
50
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { LoggingOptions, Migration, MigrationDefinition, MigrationResult, MigrationStorage, MigratorDialect, MigratorOptions, Querier, QuerierPool, SchemaDiff, SchemaGenerator, SchemaIntrospector, SqlQuerier, SyncOptions, Type } from '../type/index.js';
|
|
2
2
|
import { LoggerWrapper } from '../util/index.js';
|
|
3
3
|
import type { IMigrationBuilder } from './builder/types.js';
|
|
4
4
|
/**
|
|
@@ -13,24 +13,13 @@ export declare class Migrator {
|
|
|
13
13
|
set logger(value: LoggingOptions);
|
|
14
14
|
private readonly _entities?;
|
|
15
15
|
get entities(): Type<object>[];
|
|
16
|
-
|
|
16
|
+
/** The generator given, or this dialect's once {@link getSchemaGenerator} has loaded it. */
|
|
17
17
|
schemaGenerator?: SchemaGenerator;
|
|
18
|
-
schemaIntrospector
|
|
19
|
-
private readonly
|
|
20
|
-
private _mongoSchemaLoadPromise?;
|
|
18
|
+
schemaIntrospector: SchemaIntrospector;
|
|
19
|
+
private readonly target;
|
|
21
20
|
constructor(pool: QuerierPool<Querier, MigratorDialect>, options?: MigratorOptions);
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
* generators are set in the constructor (or via {@link setSchemaGenerator}).
|
|
25
|
-
*/
|
|
26
|
-
ensureSchemaGenerator(): Promise<void>;
|
|
27
|
-
/**
|
|
28
|
-
* Set the schema generator for DDL operations
|
|
29
|
-
*/
|
|
30
|
-
setSchemaGenerator(generator: SchemaGenerator): void;
|
|
31
|
-
/** `schema` reads one namespace instead of the connection's own; see {@link BaseSqlIntrospector.schema}. */
|
|
32
|
-
protected createIntrospector(schema?: string): SchemaIntrospector | undefined;
|
|
33
|
-
protected createGenerator(): SchemaGenerator | undefined;
|
|
21
|
+
/** The schema generator, loaded on first use: MongoDB's needs its optional peer. */
|
|
22
|
+
getSchemaGenerator(): Promise<SchemaGenerator>;
|
|
34
23
|
/**
|
|
35
24
|
* Get all discovered migrations from the migrations directory
|
|
36
25
|
*/
|
|
@@ -69,14 +58,6 @@ export declare class Migrator {
|
|
|
69
58
|
* Run a single migration, within a transaction where the dialect has one for it
|
|
70
59
|
*/
|
|
71
60
|
runMigration(migration: Migration<Querier>, direction: 'up' | 'down'): Promise<MigrationResult>;
|
|
72
|
-
/**
|
|
73
|
-
* A migration querier, and how to run work in one transaction on it. MongoDB gets none: it creates
|
|
74
|
-
* collections and indexes outside any transaction. SQL asserts its querier before opening one, so a
|
|
75
|
-
* wrong querier reports which one the dialect needs rather than a missing `transaction`.
|
|
76
|
-
*/
|
|
77
|
-
private withMigrationQuerier;
|
|
78
|
-
/** What this dialect's migration files are written against. */
|
|
79
|
-
private get migrationQuerier();
|
|
80
61
|
/**
|
|
81
62
|
* Generate a new migration file
|
|
82
63
|
*/
|
|
@@ -99,7 +80,6 @@ export declare class Migrator {
|
|
|
99
80
|
* {@link schemaIntrospector} so a caller that replaced it still wins.
|
|
100
81
|
*/
|
|
101
82
|
private introspectClaimedSchemas;
|
|
102
|
-
findEntityForTable(tableName: string): Promise<Type<object> | undefined>;
|
|
103
83
|
/**
|
|
104
84
|
* Applies the entity schema to the database: every registered entity, or the one `entity` names.
|
|
105
85
|
*
|
|
@@ -123,14 +103,12 @@ export declare class Migrator {
|
|
|
123
103
|
* settle instead of colliding; an existing one still pays for introspection, as a diff needs columns.
|
|
124
104
|
*/
|
|
125
105
|
private planEntity;
|
|
126
|
-
/** An alter diff as statements, narrowed to what the caller allows. */
|
|
127
|
-
private alterFromDiff;
|
|
128
106
|
/** The same for one entity against the table it already has, and nothing where the two agree. */
|
|
129
107
|
private alterFromEntity;
|
|
130
108
|
/** The configured entities, with `entity` among them however the migrator was built. */
|
|
131
109
|
private entitiesWith;
|
|
132
110
|
/** The introspector for a claimed schema, which is the connection's own where none is claimed. */
|
|
133
|
-
private
|
|
111
|
+
private schemaIntrospectorFor;
|
|
134
112
|
/**
|
|
135
113
|
* The DDL {@link sync} would run, without running it. Separate so `--dry-run` shows the real
|
|
136
114
|
* statements rather than a summary of a second, differently-computed diff.
|
|
@@ -146,30 +124,19 @@ export declare class Migrator {
|
|
|
146
124
|
*/
|
|
147
125
|
private createSchema;
|
|
148
126
|
/**
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
* `ALTER COLUMN`), so computing it eagerly for everyone would throw there.
|
|
153
|
-
*/
|
|
154
|
-
private pendingDiffs;
|
|
155
|
-
/**
|
|
156
|
-
* The schema generator. A getter because MongoDB's loads lazily (see {@link ensureSchemaGenerator}),
|
|
157
|
-
* so every caller had to repeat the same assertion after awaiting it.
|
|
127
|
+
* The pending diffs a sync or a generated migration acts on: the tables to create and the tables to
|
|
128
|
+
* alter. What to emit for each stays with the caller: a sync narrows an alter to what it allows and
|
|
129
|
+
* never asks for the rollback, which on SQLite cannot even be expressed (no `ALTER COLUMN`).
|
|
158
130
|
*/
|
|
159
|
-
private
|
|
131
|
+
private pendingChanges;
|
|
160
132
|
protected filterDiff(diff: SchemaDiff, options: {
|
|
161
133
|
safe?: boolean;
|
|
162
134
|
drop?: boolean;
|
|
163
135
|
}): SchemaDiff;
|
|
136
|
+
/** Runs the statements a generator wrote, in one transaction where the engine takes DDL in one. */
|
|
164
137
|
executeSyncStatements(statements: string[], options: {
|
|
165
138
|
logging?: boolean;
|
|
166
139
|
}): Promise<void>;
|
|
167
|
-
executeMongoSyncStatements(statements: string[], options: {
|
|
168
|
-
logging?: boolean;
|
|
169
|
-
}, querier: MongoQuerier): Promise<void>;
|
|
170
|
-
executeSqlSyncStatements(statements: string[], options: {
|
|
171
|
-
logging?: boolean;
|
|
172
|
-
}, querier: Querier): Promise<void>;
|
|
173
140
|
/**
|
|
174
141
|
* Get migration status
|
|
175
142
|
*/
|
|
@@ -208,12 +175,13 @@ export declare class Migrator {
|
|
|
208
175
|
export declare function defineMigration<Q extends Querier = SqlQuerier>(migration: MigrationDefinition<Q>): MigrationDefinition<Q>;
|
|
209
176
|
/**
|
|
210
177
|
* Migration definition that uses the type-safe builder API. The querier is the builder's own, so a
|
|
211
|
-
* data backfill it runs lands in the same transaction as the schema change.
|
|
178
|
+
* data backfill it runs lands in the same transaction as the schema change. On MongoDB, `Q` is
|
|
179
|
+
* `MongoQuerier` and the builder takes collections and their indexes.
|
|
212
180
|
*/
|
|
213
|
-
export interface BuilderMigrationDefinition {
|
|
181
|
+
export interface BuilderMigrationDefinition<Q extends Querier = SqlQuerier> {
|
|
214
182
|
readonly name?: string;
|
|
215
|
-
up(builder: IMigrationBuilder, querier:
|
|
216
|
-
down(builder: IMigrationBuilder, querier:
|
|
183
|
+
up(builder: IMigrationBuilder, querier: Q): Promise<void>;
|
|
184
|
+
down(builder: IMigrationBuilder, querier: Q): Promise<void>;
|
|
217
185
|
}
|
|
218
186
|
/**
|
|
219
187
|
* Define a migration using the type-safe builder API.
|
|
@@ -234,4 +202,4 @@ export interface BuilderMigrationDefinition {
|
|
|
234
202
|
* });
|
|
235
203
|
* ```
|
|
236
204
|
*/
|
|
237
|
-
export declare function defineBuilderMigration(migration: BuilderMigrationDefinition): MigrationDefinition
|
|
205
|
+
export declare function defineBuilderMigration<Q extends Querier = SqlQuerier>(migration: BuilderMigrationDefinition<Q>): MigrationDefinition<Q>;
|