uql-orm 0.66.0 → 0.67.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/querier/httpQuerier.js +1 -8
- package/dist/browser/uql-browser.min.js.map +5 -5
- package/dist/bunSql/bunSql.util.d.ts +2 -6
- package/dist/bunSql/bunSql.util.js +2 -6
- package/dist/bunSql/bunSqlQuerier.d.ts +2 -5
- package/dist/bunSql/bunSqlQuerier.js +2 -5
- package/dist/cockroachdb/cockroachDialect.d.ts +4 -13
- package/dist/cockroachdb/cockroachDialect.js +4 -13
- package/dist/context/context.browser.js +2 -10
- package/dist/context/context.d.ts +4 -17
- package/dist/context/context.js +4 -17
- package/dist/dialect/abstractDialect.d.ts +4 -19
- package/dist/dialect/abstractDialect.js +2 -20
- package/dist/dialect/abstractSqlDialect.d.ts +47 -212
- package/dist/dialect/abstractSqlDialect.js +68 -222
- package/dist/dialect/aliases.d.ts +2 -12
- package/dist/dialect/aliases.js +4 -12
- package/dist/dialect/hydrateColumn.d.ts +2 -6
- package/dist/dialect/hydrateColumn.js +3 -13
- package/dist/dialect/jsonArrayElemMatchUtils.d.ts +1 -7
- package/dist/dialect/jsonArrayElemMatchUtils.js +1 -7
- package/dist/dialect/jsonSql.d.ts +6 -27
- package/dist/dialect/jsonSql.js +6 -27
- package/dist/dialect/mergeSqlDialect.d.ts +4 -22
- package/dist/dialect/mergeSqlDialect.js +4 -22
- package/dist/dialect/mysqlLikeSqlDialect.d.ts +11 -37
- package/dist/dialect/mysqlLikeSqlDialect.js +35 -51
- package/dist/dialect/pgLikeSqlDialect.d.ts +8 -22
- package/dist/dialect/pgLikeSqlDialect.js +36 -39
- package/dist/dialect/queryContext.d.ts +4 -22
- package/dist/dialect/queryContext.js +4 -22
- package/dist/dialect/queryJoins.d.ts +3 -12
- package/dist/dialect/queryJoins.js +3 -12
- package/dist/dialect/vectorCast.d.ts +2 -12
- package/dist/dialect/vectorCast.js +3 -19
- package/dist/dialect/vectorSqlDialect.d.ts +8 -38
- package/dist/dialect/vectorSqlDialect.js +7 -38
- package/dist/entity/decorator/bag.d.ts +6 -19
- package/dist/entity/decorator/bag.js +6 -22
- package/dist/entity/decorator/entity.d.ts +2 -7
- package/dist/entity/decorator/entity.js +2 -7
- package/dist/entity/decorator/members.d.ts +7 -30
- package/dist/entity/decorator/members.js +3 -12
- package/dist/entity/metadata/definition.d.ts +2 -18
- package/dist/entity/metadata/definition.js +6 -28
- package/dist/http/handler.d.ts +2 -14
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/libsql/libsqlDialect.d.ts +1 -8
- package/dist/libsql/libsqlDialect.js +1 -8
- package/dist/maria/mariaDialect.d.ts +3 -5
- package/dist/maria/mariaDialect.js +5 -5
- package/dist/maria/mariadbQuerier.js +2 -2
- package/dist/maria/mariadbQuerierPool.js +1 -6
- package/dist/migrate/builder/migrationBuilder.js +3 -19
- package/dist/migrate/builder/splitSqlStatements.d.ts +1 -14
- package/dist/migrate/builder/splitSqlStatements.js +2 -22
- package/dist/migrate/builder/types.d.ts +2 -15
- package/dist/migrate/cli-config.js +2 -11
- package/dist/migrate/cli.js +2 -7
- package/dist/migrate/codegen/entityCodeGenerator.d.ts +0 -15
- package/dist/migrate/codegen/entityCodeGenerator.js +15 -44
- package/dist/migrate/codegen/fieldOptionsSource.d.ts +1 -8
- package/dist/migrate/codegen/fieldOptionsSource.js +3 -22
- package/dist/migrate/ddl/indexDdl.d.ts +2 -5
- package/dist/migrate/ddl/indexDdl.js +2 -5
- package/dist/migrate/ddl/pgIndexDdl.d.ts +3 -13
- package/dist/migrate/ddl/pgIndexDdl.js +3 -13
- package/dist/migrate/generator/definitionToNode.d.ts +2 -9
- package/dist/migrate/generator/definitionToNode.js +3 -17
- package/dist/migrate/generator/indexNodeToSchema.d.ts +2 -3
- package/dist/migrate/generator/indexNodeToSchema.js +2 -3
- package/dist/migrate/generator/mongoCommand.d.ts +1 -8
- package/dist/migrate/generator/mongoSchemaGenerator.d.ts +1 -8
- package/dist/migrate/generator/mongoSchemaGenerator.js +1 -8
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +6 -26
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +9 -41
- package/dist/migrate/introspection/baseSqlIntrospector.js +0 -1
- package/dist/migrate/introspection/mongoIntrospector.d.ts +3 -1
- package/dist/migrate/introspection/mongoIntrospector.js +48 -46
- package/dist/migrate/introspection/mssqlIntrospector.d.ts +4 -4
- package/dist/migrate/introspection/mssqlIntrospector.js +18 -27
- package/dist/migrate/introspection/mysqlIntrospector.d.ts +7 -2
- package/dist/migrate/introspection/mysqlIntrospector.js +16 -14
- package/dist/migrate/introspection/postgresIntrospector.d.ts +24 -9
- package/dist/migrate/introspection/postgresIntrospector.js +68 -59
- package/dist/migrate/introspection/sqliteIntrospector.d.ts +1 -1
- package/dist/migrate/introspection/sqliteIntrospector.js +8 -10
- package/dist/migrate/migrator.d.ts +9 -53
- package/dist/migrate/migrator.js +32 -65
- package/dist/migrate/schemaGenerator.d.ts +16 -66
- package/dist/migrate/schemaGenerator.js +21 -74
- package/dist/mongo/mongoDialect.d.ts +21 -53
- package/dist/mongo/mongoDialect.js +25 -70
- package/dist/mongo/mongodbQuerier.d.ts +5 -8
- package/dist/mongo/mongodbQuerier.js +31 -65
- package/dist/mssql/mssqlDialect.d.ts +8 -34
- package/dist/mssql/mssqlDialect.js +37 -51
- package/dist/mssql/mssqlQuerier.d.ts +37 -4
- package/dist/mssql/mssqlQuerier.js +2 -2
- package/dist/mssql/mssqlWireTypes.d.ts +2 -14
- package/dist/mssql/mssqlWireTypes.js +2 -14
- package/dist/nestjs/uqlModule.js +2 -7
- package/dist/pglite/pgliteQuerier.d.ts +1 -9
- package/dist/pglite/pgliteQuerierPool.d.ts +4 -26
- package/dist/pglite/pgliteQuerierPool.js +3 -18
- package/dist/postgres/abstractPgQuerierPool.d.ts +1 -8
- package/dist/postgres/abstractPgQuerierPool.js +1 -8
- package/dist/postgres/pgNumericTypes.d.ts +3 -26
- package/dist/postgres/pgNumericTypes.js +3 -26
- package/dist/postgres/postgresDialect.d.ts +4 -10
- package/dist/postgres/postgresDialect.js +4 -10
- package/dist/querier/abstractQuerier.d.ts +35 -103
- package/dist/querier/abstractQuerier.js +105 -201
- package/dist/querier/abstractSharedHandleQuerierPool.d.ts +3 -17
- package/dist/querier/abstractSharedHandleQuerierPool.js +3 -17
- package/dist/querier/abstractSqlQuerier.d.ts +15 -36
- package/dist/querier/abstractSqlQuerier.js +49 -131
- package/dist/schema/canonicalType.d.ts +3 -21
- package/dist/schema/canonicalType.js +22 -67
- package/dist/schema/dependencyGraph.d.ts +2 -8
- package/dist/schema/dependencyGraph.js +2 -32
- package/dist/schema/index.d.ts +1 -25
- package/dist/schema/index.js +0 -26
- package/dist/schema/indexColumns.d.ts +1 -8
- package/dist/schema/indexColumns.js +1 -8
- package/dist/schema/indexDifferences.d.ts +7 -40
- package/dist/schema/indexDifferences.js +6 -31
- package/dist/schema/schemaAST.d.ts +8 -175
- package/dist/schema/schemaAST.js +13 -365
- package/dist/schema/schemaASTBuilder.d.ts +2 -24
- package/dist/schema/schemaASTBuilder.js +2 -30
- package/dist/schema/schemaASTDiffer.d.ts +6 -46
- package/dist/schema/schemaASTDiffer.js +8 -56
- package/dist/schema/types.d.ts +5 -61
- package/dist/schema/types.js +3 -6
- package/dist/sqlite/abstractSqliteQuerier.d.ts +1 -8
- package/dist/sqlite/localSqliteQuerierPool.d.ts +1 -7
- package/dist/sqlite/localSqliteQuerierPool.js +1 -7
- package/dist/sqlite/nodeSqliteQuerierPool.d.ts +2 -7
- package/dist/sqlite/nodeSqliteQuerierPool.js +2 -7
- package/dist/sqlite/sqliteDialect.d.ts +5 -20
- package/dist/sqlite/sqliteDialect.js +29 -35
- package/dist/turso/tursoDialect.d.ts +4 -6
- package/dist/turso/tursoDialect.js +4 -6
- package/dist/turso/tursoLocalQuerierPool.d.ts +1 -7
- package/dist/turso/tursoLocalQuerierPool.js +1 -7
- package/dist/turso/tursoQuerierPool.d.ts +2 -6
- package/dist/turso/tursoQuerierPool.js +2 -6
- package/dist/turso/tursoSessionQuerier.d.ts +1 -7
- package/dist/turso/tursoSessionQuerier.js +1 -7
- package/dist/type/dialect.d.ts +42 -94
- package/dist/type/dialect.js +3 -13
- package/dist/type/entity.d.ts +163 -534
- package/dist/type/entity.js +26 -9
- package/dist/type/logger.d.ts +2 -14
- package/dist/type/migration.d.ts +9 -38
- package/dist/type/querier.d.ts +9 -28
- package/dist/type/querierPool.d.ts +4 -26
- package/dist/type/query.d.ts +19 -73
- package/dist/type/query.js +2 -7
- package/dist/type/queryAggregate.d.ts +18 -98
- package/dist/type/queryRaw.d.ts +1 -8
- package/dist/type/queryRaw.js +1 -8
- package/dist/type/queryWhere.d.ts +13 -61
- package/dist/type/universalQuerier.d.ts +18 -105
- package/dist/type/utility.d.ts +12 -24
- package/dist/type/vector.d.ts +8 -38
- package/dist/type/vector.js +1 -1
- package/dist/type/wire.d.ts +2 -5
- package/dist/util/dialect.util.d.ts +9 -27
- package/dist/util/dialect.util.js +10 -27
- package/dist/util/field.util.d.ts +2 -31
- package/dist/util/field.util.js +3 -43
- package/dist/util/fieldOption.util.d.ts +7 -15
- package/dist/util/fieldOption.util.js +1 -1
- package/dist/util/filters.util.d.ts +2 -5
- package/dist/util/filters.util.js +2 -5
- package/dist/util/logger.d.ts +2 -6
- package/dist/util/logger.js +2 -6
- package/dist/util/object.util.d.ts +2 -6
- package/dist/util/object.util.js +1 -5
- package/dist/util/raw.d.ts +3 -23
- package/dist/util/relationQuery.util.d.ts +3 -14
- package/dist/util/relationQuery.util.js +3 -14
- package/dist/util/rowKey.util.d.ts +2 -10
- package/dist/util/rowKey.util.js +2 -10
- package/dist/util/sql.util.d.ts +6 -37
- package/dist/util/sql.util.js +13 -73
- package/dist/util/sqlLiteral.d.ts +2 -13
- package/dist/util/sqlLiteral.js +8 -13
- package/dist/util/string.util.js +0 -2
- package/package.json +4 -4
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { unescapeMysqlString } from '../../util/sqlLiteral.js';
|
|
1
2
|
import { AbstractSqlSchemaIntrospector } from './abstractSqlSchemaIntrospector.js';
|
|
2
3
|
/**
|
|
3
4
|
* MySQL/MariaDB schema introspector.
|
|
@@ -23,9 +24,8 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
23
24
|
AND TABLE_NAME = ?
|
|
24
25
|
`;
|
|
25
26
|
}
|
|
26
|
-
parseTableExistsResult(
|
|
27
|
-
|
|
28
|
-
return Number(results[0]?.count) > 0;
|
|
27
|
+
parseTableExistsResult([row]) {
|
|
28
|
+
return Number(row.count) > 0;
|
|
29
29
|
}
|
|
30
30
|
getColumnsQuery(_tableName) {
|
|
31
31
|
return /*sql*/ `
|
|
@@ -98,7 +98,7 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
98
98
|
nullable: row.is_nullable === 'YES',
|
|
99
99
|
defaultValue: this.parseDefaultValue(row.column_default),
|
|
100
100
|
isPrimaryKey: row.column_key === 'PRI',
|
|
101
|
-
isAutoIncrement:
|
|
101
|
+
isAutoIncrement: row.extra.toLowerCase().includes('auto_increment'),
|
|
102
102
|
isUnique: row.column_key === 'UNI',
|
|
103
103
|
length: this.toNumber(row.character_maximum_length),
|
|
104
104
|
precision: this.toNumber(row.numeric_precision),
|
|
@@ -112,7 +112,7 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
112
112
|
// A functional or multi-valued key part has no `COLUMN_NAME` - the `COALESCE` above keeps its
|
|
113
113
|
// place in the list, and it is reported as the expression it is, which is what stops diffing
|
|
114
114
|
// from comparing an entry list the server cannot state against the entity's own.
|
|
115
|
-
entries:
|
|
115
|
+
entries: row.columns.split(',').map((column) => (column ? { column } : { column, expression: true })),
|
|
116
116
|
unique: Boolean(row.is_unique),
|
|
117
117
|
}));
|
|
118
118
|
}
|
|
@@ -125,6 +125,11 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
125
125
|
onUpdate: this.normalizeReferentialAction(row.update_rule),
|
|
126
126
|
}));
|
|
127
127
|
}
|
|
128
|
+
/**
|
|
129
|
+
* MariaDB prints a string default as the literal it is (`'it''s'`). MySQL prints one bare, save an
|
|
130
|
+
* expression default (`DEFAULT ('x')`, what a `TEXT` column takes), which comes with a charset
|
|
131
|
+
* introducer and every quote and backslash escaped once more: `_utf8mb4\'x\'`.
|
|
132
|
+
*/
|
|
128
133
|
parseDefaultValue(defaultValue) {
|
|
129
134
|
if (defaultValue === null) {
|
|
130
135
|
return undefined;
|
|
@@ -136,16 +141,13 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
136
141
|
if (normalized === 'CURRENT_TIMESTAMP' || normalized === 'CURRENT_TIMESTAMP()') {
|
|
137
142
|
return 'CURRENT_TIMESTAMP';
|
|
138
143
|
}
|
|
139
|
-
if (/^-?\d
|
|
140
|
-
return Number
|
|
144
|
+
if (/^-?\d+(\.\d+)?$/.test(defaultValue)) {
|
|
145
|
+
return Number(defaultValue);
|
|
141
146
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
return defaultValue.slice(1, -1);
|
|
147
|
-
}
|
|
148
|
-
return defaultValue;
|
|
147
|
+
const introduced = /^_\w+(\\'.*\\')$/s.exec(defaultValue);
|
|
148
|
+
const literal = introduced ? unescapeMysqlString(introduced[1]) : defaultValue;
|
|
149
|
+
const quoted = /^'(.*)'$/s.exec(literal);
|
|
150
|
+
return quoted ? unescapeMysqlString(quoted[1]) : literal;
|
|
149
151
|
}
|
|
150
152
|
}
|
|
151
153
|
/**
|
|
@@ -13,7 +13,7 @@ export declare class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrosp
|
|
|
13
13
|
readonly indexFacets: ReadonlySet<IndexFacet>;
|
|
14
14
|
protected getTableNamesQuery(): string;
|
|
15
15
|
protected tableExistsQuery(): string;
|
|
16
|
-
protected parseTableExistsResult(
|
|
16
|
+
protected parseTableExistsResult([row]: RawRow[]): boolean;
|
|
17
17
|
protected getColumnsQuery(_tableName: string): string;
|
|
18
18
|
/**
|
|
19
19
|
* `attname` where the entry is a column, `pg_get_indexdef` for that one position where it is an
|
|
@@ -36,22 +36,29 @@ export declare class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrosp
|
|
|
36
36
|
* compare. This one decides which indexes are reported at all.
|
|
37
37
|
*/
|
|
38
38
|
protected readonly constraintIndexTypes: readonly string[];
|
|
39
|
+
/** From `pg_constraint`, whose key arrays keep each column paired with the one it references. */
|
|
39
40
|
protected getForeignKeysQuery(_tableName: string): string;
|
|
40
41
|
protected getPrimaryKeyQuery(_tableName: string): string;
|
|
41
42
|
protected mapColumnsResult(_read: TableRowReader, _tableName: string, results: PostgresColumnRow[]): Promise<ColumnSchema[]>;
|
|
42
43
|
protected mapIndexesResult(_read: TableRowReader, _tableName: string, results: PostgresIndexRow[]): Promise<IndexSchema[]>;
|
|
43
|
-
protected mapForeignKeysResult(_read: TableRowReader, _tableName: string, results:
|
|
44
|
-
constraint_name: string;
|
|
45
|
-
columns: string[];
|
|
46
|
-
referenced_table: string;
|
|
47
|
-
referenced_columns: string[];
|
|
48
|
-
delete_rule: string;
|
|
49
|
-
update_rule: string;
|
|
50
|
-
}[]): Promise<ForeignKeySchema[]>;
|
|
44
|
+
protected mapForeignKeysResult(_read: TableRowReader, _tableName: string, results: PostgresForeignKeyRow[]): Promise<ForeignKeySchema[]>;
|
|
51
45
|
protected normalizeType(dataType: string, udtName: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Postgres quotes a negative number (`'-3'::integer`); CockroachDB parenthesizes one (`(-3)`) and
|
|
48
|
+
* writes a quote-bearing string in escape syntax (`e'it\'s'`). Any other cast is dropped, and a
|
|
49
|
+
* function call (`now()`, `nextval(...)`) is returned as written.
|
|
50
|
+
*/
|
|
52
51
|
protected parseDefaultValue(defaultValue: string | null): unknown;
|
|
53
52
|
protected isAutoIncrement(columnDefault: string | null, isIdentity: string): boolean;
|
|
54
53
|
}
|
|
54
|
+
/** `pg_constraint`'s one-letter spelling of each action. */
|
|
55
|
+
declare const FOREIGN_KEY_ACTION_CODES: {
|
|
56
|
+
readonly a: 'NO ACTION';
|
|
57
|
+
readonly r: 'RESTRICT';
|
|
58
|
+
readonly c: 'CASCADE';
|
|
59
|
+
readonly n: 'SET NULL';
|
|
60
|
+
readonly d: 'SET DEFAULT';
|
|
61
|
+
};
|
|
55
62
|
/**
|
|
56
63
|
* CockroachDB answers the same catalogue queries and differs only in what it can express: v26.2.5
|
|
57
64
|
* still rejects `NULLS FIRST/LAST` and operator classes as "unimplemented", and it sorts nulls first
|
|
@@ -69,6 +76,14 @@ export declare class CockroachSchemaIntrospector extends PostgresSchemaIntrospec
|
|
|
69
76
|
*/
|
|
70
77
|
protected readonly constraintIndexTypes: readonly string[];
|
|
71
78
|
}
|
|
79
|
+
type PostgresForeignKeyRow = {
|
|
80
|
+
constraint_name: string;
|
|
81
|
+
columns: string[];
|
|
82
|
+
referenced_table: string;
|
|
83
|
+
referenced_columns: string[];
|
|
84
|
+
delete_rule: keyof typeof FOREIGN_KEY_ACTION_CODES;
|
|
85
|
+
update_rule: keyof typeof FOREIGN_KEY_ACTION_CODES;
|
|
86
|
+
};
|
|
72
87
|
/** One entry of one index; what the index itself is repeats across its rows. */
|
|
73
88
|
type PostgresIndexRow = {
|
|
74
89
|
index_name: string;
|
|
@@ -34,8 +34,8 @@ export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
34
34
|
) AS exists
|
|
35
35
|
`;
|
|
36
36
|
}
|
|
37
|
-
parseTableExistsResult(
|
|
38
|
-
return
|
|
37
|
+
parseTableExistsResult([row]) {
|
|
38
|
+
return row['exists'] === true;
|
|
39
39
|
}
|
|
40
40
|
getColumnsQuery(_tableName) {
|
|
41
41
|
return /*sql*/ `
|
|
@@ -50,28 +50,25 @@ export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
50
50
|
c.numeric_scale,
|
|
51
51
|
c.is_identity,
|
|
52
52
|
c.identity_generation,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
LIMIT 1),
|
|
61
|
-
FALSE
|
|
53
|
+
EXISTS (
|
|
54
|
+
SELECT 1 FROM information_schema.table_constraints tc
|
|
55
|
+
JOIN information_schema.key_column_usage kcu USING (constraint_schema, constraint_name)
|
|
56
|
+
WHERE tc.table_schema = c.table_schema
|
|
57
|
+
AND tc.table_name = c.table_name
|
|
58
|
+
AND tc.constraint_type = 'PRIMARY KEY'
|
|
59
|
+
AND kcu.column_name = c.column_name
|
|
62
60
|
) AS is_primary_key,
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
FALSE
|
|
61
|
+
EXISTS (
|
|
62
|
+
SELECT 1 FROM information_schema.table_constraints tc
|
|
63
|
+
JOIN information_schema.key_column_usage kcu USING (constraint_schema, constraint_name)
|
|
64
|
+
WHERE tc.table_schema = c.table_schema
|
|
65
|
+
AND tc.table_name = c.table_name
|
|
66
|
+
AND tc.constraint_type = 'UNIQUE'
|
|
67
|
+
GROUP BY tc.constraint_name
|
|
68
|
+
HAVING COUNT(*) = 1 AND MIN(kcu.column_name) = c.column_name
|
|
72
69
|
) AS is_unique,
|
|
73
70
|
pg_catalog.col_description(
|
|
74
|
-
(
|
|
71
|
+
(quote_ident(c.table_schema) || '.' || quote_ident(c.table_name))::regclass,
|
|
75
72
|
c.ordinal_position
|
|
76
73
|
) AS column_comment
|
|
77
74
|
FROM information_schema.columns c
|
|
@@ -113,7 +110,7 @@ export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
113
110
|
LEFT JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = k.attnum AND k.attnum > 0
|
|
114
111
|
LEFT JOIN pg_opclass op ON op.oid = ix.indclass[k.n - 1]
|
|
115
112
|
WHERE t.relname = $1
|
|
116
|
-
AND n.nspname =
|
|
113
|
+
AND n.nspname = ${this.schemaExpr}
|
|
117
114
|
AND NOT ix.indisprimary
|
|
118
115
|
AND NOT EXISTS (
|
|
119
116
|
SELECT 1 FROM pg_constraint con
|
|
@@ -132,30 +129,29 @@ export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
132
129
|
* compare. This one decides which indexes are reported at all.
|
|
133
130
|
*/
|
|
134
131
|
constraintIndexTypes = ['p', 'u', 'x'];
|
|
132
|
+
/** From `pg_constraint`, whose key arrays keep each column paired with the one it references. */
|
|
135
133
|
getForeignKeysQuery(_tableName) {
|
|
134
|
+
const columnsOf = (keys, table) => /*sql*/ `ARRAY_TO_JSON(ARRAY(
|
|
135
|
+
SELECT a.attname FROM UNNEST(${keys}) WITH ORDINALITY AS k(attnum, n)
|
|
136
|
+
JOIN pg_attribute a ON a.attrelid = ${table} AND a.attnum = k.attnum
|
|
137
|
+
ORDER BY k.n
|
|
138
|
+
))`;
|
|
136
139
|
return /*sql*/ `
|
|
137
140
|
SELECT
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
FROM
|
|
145
|
-
JOIN
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
AND
|
|
151
|
-
|
|
152
|
-
ON rc.constraint_name = tc.constraint_name
|
|
153
|
-
AND rc.constraint_schema = tc.table_schema
|
|
154
|
-
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|
155
|
-
AND tc.table_name = $1
|
|
156
|
-
AND tc.table_schema = ${this.schemaExpr}
|
|
157
|
-
GROUP BY tc.constraint_name, ccu.table_name, rc.delete_rule, rc.update_rule
|
|
158
|
-
ORDER BY tc.constraint_name
|
|
141
|
+
con.conname AS constraint_name,
|
|
142
|
+
${columnsOf('con.conkey', 'con.conrelid')} AS columns,
|
|
143
|
+
ref.relname AS referenced_table,
|
|
144
|
+
${columnsOf('con.confkey', 'con.confrelid')} AS referenced_columns,
|
|
145
|
+
con.confdeltype AS delete_rule,
|
|
146
|
+
con.confupdtype AS update_rule
|
|
147
|
+
FROM pg_constraint con
|
|
148
|
+
JOIN pg_class t ON t.oid = con.conrelid
|
|
149
|
+
JOIN pg_namespace n ON n.oid = t.relnamespace
|
|
150
|
+
JOIN pg_class ref ON ref.oid = con.confrelid
|
|
151
|
+
WHERE con.contype = 'f'
|
|
152
|
+
AND t.relname = $1
|
|
153
|
+
AND n.nspname = ${this.schemaExpr}
|
|
154
|
+
ORDER BY con.conname
|
|
159
155
|
`;
|
|
160
156
|
}
|
|
161
157
|
getPrimaryKeyQuery(_tableName) {
|
|
@@ -205,8 +201,8 @@ export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
205
201
|
name: row.constraint_name,
|
|
206
202
|
columns: row.columns,
|
|
207
203
|
references: { table: row.referenced_table, columns: row.referenced_columns },
|
|
208
|
-
onDelete:
|
|
209
|
-
onUpdate:
|
|
204
|
+
onDelete: FOREIGN_KEY_ACTION_CODES[row.delete_rule],
|
|
205
|
+
onUpdate: FOREIGN_KEY_ACTION_CODES[row.update_rule],
|
|
210
206
|
}));
|
|
211
207
|
}
|
|
212
208
|
normalizeType(dataType, udtName) {
|
|
@@ -219,29 +215,32 @@ export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
219
215
|
}
|
|
220
216
|
return dataType.toUpperCase();
|
|
221
217
|
}
|
|
218
|
+
/**
|
|
219
|
+
* Postgres quotes a negative number (`'-3'::integer`); CockroachDB parenthesizes one (`(-3)`) and
|
|
220
|
+
* writes a quote-bearing string in escape syntax (`e'it\'s'`). Any other cast is dropped, and a
|
|
221
|
+
* function call (`now()`, `nextval(...)`) is returned as written.
|
|
222
|
+
*/
|
|
222
223
|
parseDefaultValue(defaultValue) {
|
|
223
224
|
if (!defaultValue) {
|
|
224
225
|
return undefined;
|
|
225
226
|
}
|
|
226
|
-
// Remove type casting (e.g., ::text, ::character varying, ::text[])
|
|
227
227
|
const cleaned = defaultValue.replace(/::[a-z_]+(\s+[a-z_]+)?(\[\])?/gi, '').trim();
|
|
228
|
-
|
|
229
|
-
|
|
228
|
+
const number = NUMBER_DEFAULT.exec(cleaned) ?? QUOTED_NUMBER_DEFAULT.exec(defaultValue);
|
|
229
|
+
if (number) {
|
|
230
|
+
return Number(number[1]);
|
|
230
231
|
}
|
|
231
|
-
|
|
232
|
-
|
|
232
|
+
const quoted = /^'(.*)'$/s.exec(cleaned);
|
|
233
|
+
if (quoted) {
|
|
234
|
+
return quoted[1].replaceAll("''", "'");
|
|
233
235
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
+
const escaped = /^e'(.*)'$/s.exec(cleaned);
|
|
237
|
+
if (escaped) {
|
|
238
|
+
return escaped[1].replace(/\\(.)/gs, '$1');
|
|
236
239
|
}
|
|
237
|
-
if (
|
|
238
|
-
return
|
|
239
|
-
}
|
|
240
|
-
if (/^-?\d+\.\d+$/.test(cleaned)) {
|
|
241
|
-
return Number.parseFloat(cleaned);
|
|
240
|
+
if (cleaned === 'true' || cleaned === 'false') {
|
|
241
|
+
return cleaned === 'true';
|
|
242
242
|
}
|
|
243
|
-
|
|
244
|
-
return cleaned;
|
|
243
|
+
return cleaned === 'NULL' ? null : cleaned;
|
|
245
244
|
}
|
|
246
245
|
isAutoIncrement(columnDefault, isIdentity) {
|
|
247
246
|
// PostgreSQL identity columns (GENERATED ... AS IDENTITY)
|
|
@@ -252,6 +251,16 @@ export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
252
251
|
return columnDefault?.includes('nextval(') ?? false;
|
|
253
252
|
}
|
|
254
253
|
}
|
|
254
|
+
/** `pg_constraint`'s one-letter spelling of each action. */
|
|
255
|
+
const FOREIGN_KEY_ACTION_CODES = {
|
|
256
|
+
a: 'NO ACTION',
|
|
257
|
+
r: 'RESTRICT',
|
|
258
|
+
c: 'CASCADE',
|
|
259
|
+
n: 'SET NULL',
|
|
260
|
+
d: 'SET DEFAULT',
|
|
261
|
+
};
|
|
262
|
+
const NUMBER_DEFAULT = /^\(?(-?\d+(?:\.\d+)?)\)?$/;
|
|
263
|
+
const QUOTED_NUMBER_DEFAULT = /^'(-?\d+(?:\.\d+)?)'::(?:smallint|integer|bigint|numeric|real|double precision)$/;
|
|
255
264
|
/**
|
|
256
265
|
* Postgres states every entry in full: a plain column still reports `order: 'asc'`, and only a
|
|
257
266
|
* non-default operator class is named. The diff defaults the entity side to match, so an option
|
|
@@ -6,7 +6,7 @@ import { AbstractSqlSchemaIntrospector, type TableRowReader } from './abstractSq
|
|
|
6
6
|
export declare class SqliteSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
7
7
|
protected getTableNamesQuery(): string;
|
|
8
8
|
protected tableExistsQuery(): string;
|
|
9
|
-
protected parseTableExistsResult(
|
|
9
|
+
protected parseTableExistsResult([row]: SqliteCountRow[]): boolean;
|
|
10
10
|
/**
|
|
11
11
|
* `table_xinfo`, not `table_info`: the latter omits generated columns entirely, so a table carrying
|
|
12
12
|
* one read back without it and every sync offered to add a column that was already there - which
|
|
@@ -21,9 +21,8 @@ export class SqliteSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
21
21
|
AND name = ?
|
|
22
22
|
`;
|
|
23
23
|
}
|
|
24
|
-
parseTableExistsResult(
|
|
25
|
-
|
|
26
|
-
return Number(results[0]?.count) > 0;
|
|
24
|
+
parseTableExistsResult([row]) {
|
|
25
|
+
return Number(row.count) > 0;
|
|
27
26
|
}
|
|
28
27
|
/**
|
|
29
28
|
* `table_xinfo`, not `table_info`: the latter omits generated columns entirely, so a table carrying
|
|
@@ -59,15 +58,16 @@ export class SqliteSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
59
58
|
return row.name;
|
|
60
59
|
}
|
|
61
60
|
async mapColumnsResult(read, tableName, results) {
|
|
62
|
-
// Get unique columns from indexes
|
|
63
61
|
const uniqueColumns = await this.getUniqueColumns(read, tableName);
|
|
62
|
+
// Only a sole `INTEGER PRIMARY KEY` is the rowid, which is what numbers itself.
|
|
63
|
+
const soleKey = results.filter((row) => row.pk > 0).length === 1;
|
|
64
64
|
return results.map((row) => ({
|
|
65
65
|
name: row.name,
|
|
66
66
|
type: this.normalizeType(row.type),
|
|
67
67
|
nullable: row.notnull === 0,
|
|
68
68
|
defaultValue: this.parseDefaultValue(row.dflt_value),
|
|
69
69
|
isPrimaryKey: row.pk > 0,
|
|
70
|
-
isAutoIncrement: row.pk > 0 && row.type.toUpperCase() === 'INTEGER',
|
|
70
|
+
isAutoIncrement: soleKey && row.pk > 0 && row.type.toUpperCase() === 'INTEGER',
|
|
71
71
|
isUnique: uniqueColumns.has(row.name),
|
|
72
72
|
length: this.extractLength(row.type),
|
|
73
73
|
precision: undefined,
|
|
@@ -130,8 +130,9 @@ export class SqliteSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
130
130
|
async getUniqueColumns(read, tableName) {
|
|
131
131
|
const indexes = await read(this.getIndexesQuery(tableName));
|
|
132
132
|
const uniqueColumns = new Set();
|
|
133
|
+
// The key's own index is left out: a key column is unique already, which the entity side never states.
|
|
133
134
|
for (const index of indexes) {
|
|
134
|
-
if (index.unique) {
|
|
135
|
+
if (index.unique && index.origin !== 'pk') {
|
|
135
136
|
const columns = await this.getIndexColumns(read, index.name);
|
|
136
137
|
// Only single-column unique constraints, and only over a real column (not an expression)
|
|
137
138
|
const [column] = columns;
|
|
@@ -165,14 +166,11 @@ export class SqliteSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
165
166
|
return defaultValue;
|
|
166
167
|
}
|
|
167
168
|
if (/^'.*'$/.test(defaultValue)) {
|
|
168
|
-
return defaultValue.slice(1, -1);
|
|
169
|
+
return defaultValue.slice(1, -1).replaceAll("''", "'");
|
|
169
170
|
}
|
|
170
171
|
if (/^-?\d+$/.test(defaultValue)) {
|
|
171
172
|
return Number.parseInt(defaultValue, 10);
|
|
172
173
|
}
|
|
173
|
-
if (typeof defaultValue !== 'string') {
|
|
174
|
-
return defaultValue;
|
|
175
|
-
}
|
|
176
174
|
if (/^-?\d+\.\d+$/.test(defaultValue)) {
|
|
177
175
|
return Number.parseFloat(defaultValue);
|
|
178
176
|
}
|
|
@@ -46,13 +46,7 @@ export declare class Migrator {
|
|
|
46
46
|
to?: string;
|
|
47
47
|
step?: number;
|
|
48
48
|
}): Promise<MigrationResult[]>;
|
|
49
|
-
/**
|
|
50
|
-
* Narrow a run list by `to`/`step` and execute it, stopping at the first failure.
|
|
51
|
-
*
|
|
52
|
-
* Both directions do exactly this and differ only in the list they start from: `up` takes the
|
|
53
|
-
* pending migrations, `down` the executed ones reversed. Keeping the selection in one place is what
|
|
54
|
-
* makes `--to` and `--step` mean the same thing whichever way you are going.
|
|
55
|
-
*/
|
|
49
|
+
/** Runs the list narrowed by `to`/`step`, stopping at the first failure: `up` over the pending, `down` over the executed reversed. */
|
|
56
50
|
private runInOrder;
|
|
57
51
|
/**
|
|
58
52
|
* Run a single migration, within a transaction where the dialect has one for it
|
|
@@ -73,29 +67,13 @@ export declare class Migrator {
|
|
|
73
67
|
*/
|
|
74
68
|
getDiffs(): Promise<SchemaDiff[]>;
|
|
75
69
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* that wants it spells the key: `undefined` on both sides for the connection's default, a name on
|
|
79
|
-
* both sides otherwise. Ordinarily that is one schema and one pass, as before, and that pass keeps
|
|
80
|
-
* {@link schemaIntrospector} so a caller that replaced it still wins.
|
|
81
|
-
*/
|
|
82
|
-
private introspectClaimedSchemas;
|
|
83
|
-
/**
|
|
84
|
-
* Applies the entity schema to the database: every registered entity, or the one `entity` names.
|
|
85
|
-
*
|
|
86
|
-
* The whole surface is this and {@link planSync}, which answers the same question without running
|
|
87
|
-
* it - `force` and a single entity included, so `--dry-run` means the same thing whatever else was
|
|
88
|
-
* asked for.
|
|
70
|
+
* The tables `entities` name, read a schema at a time so each is keyed as its entity spells it. Those
|
|
71
|
+
* alone: nothing else is diffed, and another table can be dropped mid-scan by whatever else is running.
|
|
89
72
|
*/
|
|
73
|
+
private introspectEntities;
|
|
74
|
+
/** Applies the entity schema: every entity, or the one `entity` names. {@link planSync} answers the same without running it. */
|
|
90
75
|
sync(options?: SyncOptions): Promise<void>;
|
|
91
|
-
/**
|
|
92
|
-
* Every table dropped and recreated.
|
|
93
|
-
*
|
|
94
|
-
* Both directions span the whole entity set rather than looping an entity at a time. A per-entity
|
|
95
|
-
* AST cannot resolve a cross-entity foreign key, so the old create loop silently produced a schema
|
|
96
|
-
* with no referential integrity; and the old drop loop went in reverse *declaration* order, which
|
|
97
|
-
* says nothing about the relation graph and is rejected as soon as the constraints are really there.
|
|
98
|
-
*/
|
|
76
|
+
/** Every table dropped and recreated, the whole entity set at once, so foreign keys resolve and drop in graph order. */
|
|
99
77
|
private forceStatements;
|
|
100
78
|
/**
|
|
101
79
|
* The DDL for one entity: {@link planSync} narrowed to the table it names. A new table costs one
|
|
@@ -114,14 +92,7 @@ export declare class Migrator {
|
|
|
114
92
|
* statements rather than a summary of a second, differently-computed diff.
|
|
115
93
|
*/
|
|
116
94
|
planSync(options?: SyncOptions): Promise<string[]>;
|
|
117
|
-
/**
|
|
118
|
-
* New tables are emitted together, never one at a time: a single-entity AST has no other table for a
|
|
119
|
-
* relation to resolve against, so every cross-entity foreign key was dropped and generated schemas
|
|
120
|
-
* carried none. Spanning the graph is also what lets a cyclic relation (any `createdBy`
|
|
121
|
-
* back-reference) be created at all.
|
|
122
|
-
*
|
|
123
|
-
* Empty in, empty out, so a diff with no new tables does not build an AST for the whole graph.
|
|
124
|
-
*/
|
|
95
|
+
/** The new tables, created together so a foreign key between them, cyclic included, resolves. Empty in, empty out. */
|
|
125
96
|
private createSchema;
|
|
126
97
|
/**
|
|
127
98
|
* The pending diffs a sync or a generated migration acts on: the tables to create and the tables to
|
|
@@ -184,22 +155,7 @@ export interface BuilderMigrationDefinition<Q extends Querier = SqlQuerier> {
|
|
|
184
155
|
down(builder: IMigrationBuilder, querier: Q): Promise<void>;
|
|
185
156
|
}
|
|
186
157
|
/**
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
* @example
|
|
190
|
-
* ```ts
|
|
191
|
-
* export default defineBuilderMigration({
|
|
192
|
-
* async up(m) {
|
|
193
|
-
* await m.createTable('users', (t) => {
|
|
194
|
-
* t.id();
|
|
195
|
-
* t.string('email', { length: 255 }).unique();
|
|
196
|
-
* t.timestamps();
|
|
197
|
-
* });
|
|
198
|
-
* },
|
|
199
|
-
* async down(m) {
|
|
200
|
-
* await m.dropTable('users');
|
|
201
|
-
* }
|
|
202
|
-
* });
|
|
203
|
-
* ```
|
|
158
|
+
* Defines a migration with the builder:
|
|
159
|
+
* `defineBuilderMigration({ up: (m) => m.createTable('users', (t) => t.id()), down: (m) => m.dropTable('users') })`.
|
|
204
160
|
*/
|
|
205
161
|
export declare function defineBuilderMigration<Q extends Querier = SqlQuerier>(migration: BuilderMigrationDefinition<Q>): MigrationDefinition<Q>;
|
package/dist/migrate/migrator.js
CHANGED
|
@@ -2,7 +2,7 @@ import { mkdir, readdir, writeFile } from 'node:fs/promises';
|
|
|
2
2
|
import { basename, extname, join } from 'node:path';
|
|
3
3
|
import { pathToFileURL } from 'node:url';
|
|
4
4
|
import { getEntities, getMeta } from '../entity/index.js';
|
|
5
|
-
import {
|
|
5
|
+
import { SchemaAST } from '../schema/index.js';
|
|
6
6
|
import { LoggerWrapper } from '../util/index.js';
|
|
7
7
|
import { buildMigrationModule } from './codegen/migrationFile.js';
|
|
8
8
|
import { introspectorFor } from './introspection/registry.js';
|
|
@@ -88,13 +88,7 @@ export class Migrator {
|
|
|
88
88
|
const executedMigrations = migrations.filter((m) => executedSet.has(m.name)).reverse(); // Rollback in reverse order
|
|
89
89
|
return this.runInOrder(executedMigrations, 'down', options);
|
|
90
90
|
}
|
|
91
|
-
/**
|
|
92
|
-
* Narrow a run list by `to`/`step` and execute it, stopping at the first failure.
|
|
93
|
-
*
|
|
94
|
-
* Both directions do exactly this and differ only in the list they start from: `up` takes the
|
|
95
|
-
* pending migrations, `down` the executed ones reversed. Keeping the selection in one place is what
|
|
96
|
-
* makes `--to` and `--step` mean the same thing whichever way you are going.
|
|
97
|
-
*/
|
|
91
|
+
/** Runs the list narrowed by `to`/`step`, stopping at the first failure: `up` over the pending, `down` over the executed reversed. */
|
|
98
92
|
async runInOrder(migrations, direction, options) {
|
|
99
93
|
let selected = migrations;
|
|
100
94
|
if (options.to) {
|
|
@@ -211,7 +205,7 @@ export class Migrator {
|
|
|
211
205
|
*/
|
|
212
206
|
async getDiffs() {
|
|
213
207
|
const generator = await this.getSchemaGenerator();
|
|
214
|
-
const ast = await this.
|
|
208
|
+
const ast = await this.introspectEntities(this.entities);
|
|
215
209
|
// Both sides built once: the database's above, the entities' here. Left to `diffSchema`, each
|
|
216
210
|
// entity would rebuild the whole AST, which is quadratic in the number of entities. Absent on a
|
|
217
211
|
// generator that compares no schema of its own - MongoDB, which reads only indexes.
|
|
@@ -223,29 +217,22 @@ export class Migrator {
|
|
|
223
217
|
});
|
|
224
218
|
}
|
|
225
219
|
/**
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
* that wants it spells the key: `undefined` on both sides for the connection's default, a name on
|
|
229
|
-
* both sides otherwise. Ordinarily that is one schema and one pass, as before, and that pass keeps
|
|
230
|
-
* {@link schemaIntrospector} so a caller that replaced it still wins.
|
|
220
|
+
* The tables `entities` name, read a schema at a time so each is keyed as its entity spells it. Those
|
|
221
|
+
* alone: nothing else is diffed, and another table can be dropped mid-scan by whatever else is running.
|
|
231
222
|
*/
|
|
232
|
-
async
|
|
233
|
-
const
|
|
223
|
+
async introspectEntities(entities) {
|
|
224
|
+
const { dialect } = this.pool;
|
|
225
|
+
const bySchema = Map.groupBy(new Set(entities), (entity) => dialect.resolveSchema(getMeta(entity)));
|
|
234
226
|
const merged = new SchemaAST();
|
|
235
|
-
for (const schema of
|
|
236
|
-
|
|
227
|
+
for (const [schema, members] of bySchema) {
|
|
228
|
+
const tables = members.map((entity) => dialect.resolveTableAlias(getMeta(entity)));
|
|
229
|
+
for (const table of (await this.schemaIntrospectorFor(schema).introspect(tables)).getTables()) {
|
|
237
230
|
merged.addTable(table);
|
|
238
231
|
}
|
|
239
232
|
}
|
|
240
233
|
return merged;
|
|
241
234
|
}
|
|
242
|
-
/**
|
|
243
|
-
* Applies the entity schema to the database: every registered entity, or the one `entity` names.
|
|
244
|
-
*
|
|
245
|
-
* The whole surface is this and {@link planSync}, which answers the same question without running
|
|
246
|
-
* it - `force` and a single entity included, so `--dry-run` means the same thing whatever else was
|
|
247
|
-
* asked for.
|
|
248
|
-
*/
|
|
235
|
+
/** Applies the entity schema: every entity, or the one `entity` names. {@link planSync} answers the same without running it. */
|
|
249
236
|
async sync(options = {}) {
|
|
250
237
|
const statements = await this.planSync(options);
|
|
251
238
|
if (statements.length) {
|
|
@@ -255,14 +242,7 @@ export class Migrator {
|
|
|
255
242
|
this.logger.logSchema('Schema is already in sync.');
|
|
256
243
|
}
|
|
257
244
|
}
|
|
258
|
-
/**
|
|
259
|
-
* Every table dropped and recreated.
|
|
260
|
-
*
|
|
261
|
-
* Both directions span the whole entity set rather than looping an entity at a time. A per-entity
|
|
262
|
-
* AST cannot resolve a cross-entity foreign key, so the old create loop silently produced a schema
|
|
263
|
-
* with no referential integrity; and the old drop loop went in reverse *declaration* order, which
|
|
264
|
-
* says nothing about the relation graph and is rejected as soon as the constraints are really there.
|
|
265
|
-
*/
|
|
245
|
+
/** Every table dropped and recreated, the whole entity set at once, so foreign keys resolve and drop in graph order. */
|
|
266
246
|
forceStatements(generator) {
|
|
267
247
|
return [
|
|
268
248
|
...generator.generateDropSchema(this.entities, { ifExists: true, cascade: true }),
|
|
@@ -276,14 +256,17 @@ export class Migrator {
|
|
|
276
256
|
*/
|
|
277
257
|
async planEntity(generator, entity, options) {
|
|
278
258
|
const meta = getMeta(entity);
|
|
279
|
-
const
|
|
259
|
+
const { dialect } = this.pool;
|
|
260
|
+
const introspector = this.schemaIntrospectorFor(dialect.resolveSchema(meta));
|
|
280
261
|
const tableName = generator.resolveTableName(meta);
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
262
|
+
if (!(await introspector.tableExists(dialect.resolveTableAlias(meta)))) {
|
|
263
|
+
// Spanning the whole set, so a foreign key resolves against the tables it points at, and always
|
|
264
|
+
// including this entity: `only` is what keeps the statements to this table.
|
|
265
|
+
return generator.generateCreateSchema(this.entitiesWith(entity), { only: [tableName], ifNotExists: true });
|
|
266
|
+
}
|
|
267
|
+
// With the tables it references, which its foreign keys resolve against.
|
|
268
|
+
const ast = await this.introspectEntities([entity, ...referencedEntities(meta)]);
|
|
269
|
+
return this.alterFromEntity(generator, entity, ast.getTable(tableName), options);
|
|
287
270
|
}
|
|
288
271
|
/** The same for one entity against the table it already has, and nothing where the two agree. */
|
|
289
272
|
alterFromEntity(generator, entity, table, options) {
|
|
@@ -319,14 +302,7 @@ export class Migrator {
|
|
|
319
302
|
...altered.flatMap((diff) => generator.generateAlterTable(this.filterDiff(diff, options))),
|
|
320
303
|
];
|
|
321
304
|
}
|
|
322
|
-
/**
|
|
323
|
-
* New tables are emitted together, never one at a time: a single-entity AST has no other table for a
|
|
324
|
-
* relation to resolve against, so every cross-entity foreign key was dropped and generated schemas
|
|
325
|
-
* carried none. Spanning the graph is also what lets a cyclic relation (any `createdBy`
|
|
326
|
-
* back-reference) be created at all.
|
|
327
|
-
*
|
|
328
|
-
* Empty in, empty out, so a diff with no new tables does not build an AST for the whole graph.
|
|
329
|
-
*/
|
|
305
|
+
/** The new tables, created together so a foreign key between them, cyclic included, resolves. Empty in, empty out. */
|
|
330
306
|
createSchema(generator, tableNames) {
|
|
331
307
|
return tableNames.length ? generator.generateCreateSchema(this.entities, { only: tableNames }) : [];
|
|
332
308
|
}
|
|
@@ -484,23 +460,8 @@ export function defineMigration(migration) {
|
|
|
484
460
|
return migration;
|
|
485
461
|
}
|
|
486
462
|
/**
|
|
487
|
-
*
|
|
488
|
-
*
|
|
489
|
-
* @example
|
|
490
|
-
* ```ts
|
|
491
|
-
* export default defineBuilderMigration({
|
|
492
|
-
* async up(m) {
|
|
493
|
-
* await m.createTable('users', (t) => {
|
|
494
|
-
* t.id();
|
|
495
|
-
* t.string('email', { length: 255 }).unique();
|
|
496
|
-
* t.timestamps();
|
|
497
|
-
* });
|
|
498
|
-
* },
|
|
499
|
-
* async down(m) {
|
|
500
|
-
* await m.dropTable('users');
|
|
501
|
-
* }
|
|
502
|
-
* });
|
|
503
|
-
* ```
|
|
463
|
+
* Defines a migration with the builder:
|
|
464
|
+
* `defineBuilderMigration({ up: (m) => m.createTable('users', (t) => t.id()), down: (m) => m.dropTable('users') })`.
|
|
504
465
|
*/
|
|
505
466
|
export function defineBuilderMigration(migration) {
|
|
506
467
|
return {
|
|
@@ -509,3 +470,9 @@ export function defineBuilderMigration(migration) {
|
|
|
509
470
|
down: async (querier) => migration.down(await migrationBuilderFor(querier), querier),
|
|
510
471
|
};
|
|
511
472
|
}
|
|
473
|
+
/** The entities `meta` points at, through a relation or a foreign key field. */
|
|
474
|
+
function referencedEntities(meta) {
|
|
475
|
+
const fields = Object.values(meta.fields).flatMap((field) => field?.references?.() ?? []);
|
|
476
|
+
const relations = Object.values(meta.relations).flatMap((relation) => relation?.entity?.() ?? []);
|
|
477
|
+
return [...fields, ...relations];
|
|
478
|
+
}
|