uql-orm 0.34.0 → 0.35.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 +1 -1
- package/dist/dialect/abstractDialect.js +2 -3
- package/dist/dialect/abstractSqlDialect.d.ts +6 -0
- package/dist/dialect/abstractSqlDialect.js +10 -3
- package/dist/migrate/builder/expressions.d.ts +8 -28
- package/dist/migrate/builder/expressions.js +8 -40
- package/dist/migrate/builder/migrationBuilder.js +3 -1
- package/dist/migrate/builder/tableBuilder.js +5 -4
- package/dist/migrate/generator/definitionToNode.js +3 -3
- package/dist/migrate/generator/mongoSchemaGenerator.js +3 -2
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +12 -1
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +16 -2
- package/dist/migrate/introspection/baseSqlIntrospector.d.ts +7 -1
- package/dist/migrate/introspection/baseSqlIntrospector.js +11 -12
- package/dist/migrate/introspection/mongoIntrospector.js +0 -1
- package/dist/migrate/introspection/mysqlIntrospector.d.ts +1 -0
- package/dist/migrate/introspection/mysqlIntrospector.js +8 -6
- package/dist/migrate/introspection/postgresIntrospector.d.ts +1 -0
- package/dist/migrate/introspection/postgresIntrospector.js +6 -5
- package/dist/migrate/migrator.d.ts +14 -5
- package/dist/migrate/migrator.js +32 -10
- package/dist/migrate/schemaGenerator.d.ts +12 -8
- package/dist/migrate/schemaGenerator.js +44 -29
- package/dist/schema/schemaAST.d.ts +8 -1
- package/dist/schema/schemaAST.js +22 -13
- package/dist/schema/schemaASTBuilder.d.ts +4 -2
- package/dist/schema/schemaASTBuilder.js +17 -25
- package/dist/schema/types.d.ts +10 -3
- package/dist/type/migration.d.ts +15 -1
- package/dist/util/sql.util.d.ts +17 -0
- package/dist/util/sql.util.js +23 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { applyFilters, buildQueryWhereAsMap } from '../util/dialect.util.js';
|
|
2
|
+
import { qualifyName } from '../util/sql.util.js';
|
|
2
3
|
/**
|
|
3
4
|
* The dialect's share of a pool's {@link ExtraOptions}: what changes the SQL rather than the
|
|
4
5
|
* connection. Every pool builds its dialect through this, so a new option lands here instead of in
|
|
@@ -52,9 +53,7 @@ export class AbstractDialect {
|
|
|
52
53
|
* would point at one that does not exist.
|
|
53
54
|
*/
|
|
54
55
|
resolveTableName(meta) {
|
|
55
|
-
|
|
56
|
-
const schema = this.resolveSchema(meta);
|
|
57
|
-
return schema ? `${schema}.${name}` : name;
|
|
56
|
+
return qualifyName(this.resolveTableAlias(meta), this.resolveSchema(meta));
|
|
58
57
|
}
|
|
59
58
|
/**
|
|
60
59
|
* Which schema an entity is read from: its own wins over the pool's default, the way its `name`
|
|
@@ -301,6 +301,12 @@ export declare abstract class AbstractSqlDialect extends IndexSqlDialect impleme
|
|
|
301
301
|
protected getUpsertConflictPathsStr<E>(meta: EntityMeta<E>, conflictPaths: QueryConflictPaths<E>): string;
|
|
302
302
|
delete<E>(ctx: QueryContext, entity: Type<E>, q: QuerySearch<E>, opts?: QueryOptions): void;
|
|
303
303
|
escapeId(val: string | undefined, forbidQualified?: boolean, addDot?: boolean): string;
|
|
304
|
+
/**
|
|
305
|
+
* A name behind its schema, each part escaped on its own rather than as one dotted string taken
|
|
306
|
+
* apart again by {@link escapeId}. Tables and their indexes both use it, since an index lives in
|
|
307
|
+
* the schema of the table it is on.
|
|
308
|
+
*/
|
|
309
|
+
escapeQualifiedId(name: string, schema: string | undefined): string;
|
|
304
310
|
/**
|
|
305
311
|
* Bind one persisted value, classifying its column on the spot. Dialects override
|
|
306
312
|
* {@link appendJsonValue} and {@link appendVectorValue} rather than this, so the chain runs once per
|
|
@@ -195,9 +195,7 @@ export class AbstractSqlDialect extends IndexSqlDialect {
|
|
|
195
195
|
* string taken apart again by {@link escapeId}.
|
|
196
196
|
*/
|
|
197
197
|
escapedTableName(meta) {
|
|
198
|
-
|
|
199
|
-
const schema = this.resolveSchema(meta);
|
|
200
|
-
return schema ? `${this.escapeId(schema, true)}.${alias}` : alias;
|
|
198
|
+
return this.escapeQualifiedId(this.resolveTableAlias(meta), this.resolveSchema(meta));
|
|
201
199
|
}
|
|
202
200
|
/**
|
|
203
201
|
* A FROM or JOIN operand plus the alias to prefix its columns by, aliased only once a schema puts
|
|
@@ -1065,6 +1063,15 @@ export class AbstractSqlDialect extends IndexSqlDialect {
|
|
|
1065
1063
|
escapeId(val, forbidQualified, addDot) {
|
|
1066
1064
|
return escapeSqlId(val, this.escapeIdChar, forbidQualified, addDot);
|
|
1067
1065
|
}
|
|
1066
|
+
/**
|
|
1067
|
+
* A name behind its schema, each part escaped on its own rather than as one dotted string taken
|
|
1068
|
+
* apart again by {@link escapeId}. Tables and their indexes both use it, since an index lives in
|
|
1069
|
+
* the schema of the table it is on.
|
|
1070
|
+
*/
|
|
1071
|
+
escapeQualifiedId(name, schema) {
|
|
1072
|
+
const escaped = this.escapeId(name, true);
|
|
1073
|
+
return schema ? `${this.escapeId(schema, true)}.${escaped}` : escaped;
|
|
1074
|
+
}
|
|
1068
1075
|
/**
|
|
1069
1076
|
* Bind one persisted value, classifying its column on the spot. Dialects override
|
|
1070
1077
|
* {@link appendJsonValue} and {@link appendVectorValue} rather than this, so the chain runs once per
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* SQL Expressions
|
|
3
3
|
*
|
|
4
4
|
* Type-safe SQL expressions for default values and other uses.
|
|
5
|
-
* Provides
|
|
5
|
+
* Provides an `expr` helper with common expressions.
|
|
6
6
|
*/
|
|
7
7
|
/**
|
|
8
8
|
* Represents a raw SQL expression (not a literal value).
|
|
@@ -18,11 +18,11 @@ export declare class SqlExpression {
|
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
20
|
* Helper object for common SQL expressions.
|
|
21
|
-
* Use in migrations: `
|
|
21
|
+
* Use in migrations: `t.timestamp('createdAt').defaultValue(expr.now())`
|
|
22
22
|
*/
|
|
23
|
-
export declare const
|
|
23
|
+
export declare const expr: {
|
|
24
24
|
/**
|
|
25
|
-
* Current timestamp (
|
|
25
|
+
* Current timestamp (CURRENT_TIMESTAMP).
|
|
26
26
|
*/
|
|
27
27
|
now(): SqlExpression;
|
|
28
28
|
/**
|
|
@@ -34,23 +34,11 @@ export declare const t: {
|
|
|
34
34
|
*/
|
|
35
35
|
currentTime(): SqlExpression;
|
|
36
36
|
/**
|
|
37
|
-
*
|
|
38
|
-
*/
|
|
39
|
-
null(): SqlExpression;
|
|
40
|
-
/**
|
|
41
|
-
* Boolean true.
|
|
42
|
-
*/
|
|
43
|
-
true(): SqlExpression;
|
|
44
|
-
/**
|
|
45
|
-
* Boolean false.
|
|
46
|
-
*/
|
|
47
|
-
false(): SqlExpression;
|
|
48
|
-
/**
|
|
49
|
-
* Generate UUID (Postgres: gen_random_uuid(), MySQL: UUID()).
|
|
37
|
+
* Generate a UUID. Postgres only - use `expr.mysqlUuid()` on MySQL/MariaDB.
|
|
50
38
|
*/
|
|
51
39
|
uuid(): SqlExpression;
|
|
52
40
|
/**
|
|
53
|
-
*
|
|
41
|
+
* Generate a UUID on MySQL/MariaDB.
|
|
54
42
|
*/
|
|
55
43
|
mysqlUuid(): SqlExpression;
|
|
56
44
|
/**
|
|
@@ -59,19 +47,11 @@ export declare const t: {
|
|
|
59
47
|
*/
|
|
60
48
|
raw(sql: string): SqlExpression;
|
|
61
49
|
/**
|
|
62
|
-
*
|
|
63
|
-
*/
|
|
64
|
-
literal(value: string): SqlExpression;
|
|
65
|
-
/**
|
|
66
|
-
* Numeric literal.
|
|
67
|
-
*/
|
|
68
|
-
number(value: number): SqlExpression;
|
|
69
|
-
/**
|
|
70
|
-
* Empty JSON object.
|
|
50
|
+
* Empty JSON object. Postgres only - the `::jsonb` cast is not portable.
|
|
71
51
|
*/
|
|
72
52
|
emptyObject(): SqlExpression;
|
|
73
53
|
/**
|
|
74
|
-
* Empty JSON array.
|
|
54
|
+
* Empty JSON array. Postgres only - the `::jsonb` cast is not portable.
|
|
75
55
|
*/
|
|
76
56
|
emptyArray(): SqlExpression;
|
|
77
57
|
/**
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* SQL Expressions
|
|
3
3
|
*
|
|
4
4
|
* Type-safe SQL expressions for default values and other uses.
|
|
5
|
-
* Provides
|
|
5
|
+
* Provides an `expr` helper with common expressions.
|
|
6
6
|
*/
|
|
7
7
|
/**
|
|
8
8
|
* Represents a raw SQL expression (not a literal value).
|
|
@@ -24,11 +24,11 @@ export class SqlExpression {
|
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
26
|
* Helper object for common SQL expressions.
|
|
27
|
-
* Use in migrations: `
|
|
27
|
+
* Use in migrations: `t.timestamp('createdAt').defaultValue(expr.now())`
|
|
28
28
|
*/
|
|
29
|
-
export const
|
|
29
|
+
export const expr = {
|
|
30
30
|
/**
|
|
31
|
-
* Current timestamp (
|
|
31
|
+
* Current timestamp (CURRENT_TIMESTAMP).
|
|
32
32
|
*/
|
|
33
33
|
now() {
|
|
34
34
|
return new SqlExpression('CURRENT_TIMESTAMP');
|
|
@@ -46,31 +46,13 @@ export const t = {
|
|
|
46
46
|
return new SqlExpression('CURRENT_TIME');
|
|
47
47
|
},
|
|
48
48
|
/**
|
|
49
|
-
*
|
|
50
|
-
*/
|
|
51
|
-
null() {
|
|
52
|
-
return new SqlExpression('NULL');
|
|
53
|
-
},
|
|
54
|
-
/**
|
|
55
|
-
* Boolean true.
|
|
56
|
-
*/
|
|
57
|
-
true() {
|
|
58
|
-
return new SqlExpression('TRUE');
|
|
59
|
-
},
|
|
60
|
-
/**
|
|
61
|
-
* Boolean false.
|
|
62
|
-
*/
|
|
63
|
-
false() {
|
|
64
|
-
return new SqlExpression('FALSE');
|
|
65
|
-
},
|
|
66
|
-
/**
|
|
67
|
-
* Generate UUID (Postgres: gen_random_uuid(), MySQL: UUID()).
|
|
49
|
+
* Generate a UUID. Postgres only - use `expr.mysqlUuid()` on MySQL/MariaDB.
|
|
68
50
|
*/
|
|
69
51
|
uuid() {
|
|
70
52
|
return new SqlExpression('gen_random_uuid()');
|
|
71
53
|
},
|
|
72
54
|
/**
|
|
73
|
-
*
|
|
55
|
+
* Generate a UUID on MySQL/MariaDB.
|
|
74
56
|
*/
|
|
75
57
|
mysqlUuid() {
|
|
76
58
|
return new SqlExpression('UUID()');
|
|
@@ -83,27 +65,13 @@ export const t = {
|
|
|
83
65
|
return new SqlExpression(sql);
|
|
84
66
|
},
|
|
85
67
|
/**
|
|
86
|
-
*
|
|
87
|
-
*/
|
|
88
|
-
literal(value) {
|
|
89
|
-
// Escape single quotes by doubling them
|
|
90
|
-
const escaped = value.replace(/'/g, "''");
|
|
91
|
-
return new SqlExpression(`'${escaped}'`);
|
|
92
|
-
},
|
|
93
|
-
/**
|
|
94
|
-
* Numeric literal.
|
|
95
|
-
*/
|
|
96
|
-
number(value) {
|
|
97
|
-
return new SqlExpression(String(value));
|
|
98
|
-
},
|
|
99
|
-
/**
|
|
100
|
-
* Empty JSON object.
|
|
68
|
+
* Empty JSON object. Postgres only - the `::jsonb` cast is not portable.
|
|
101
69
|
*/
|
|
102
70
|
emptyObject() {
|
|
103
71
|
return new SqlExpression("'{}'::jsonb");
|
|
104
72
|
},
|
|
105
73
|
/**
|
|
106
|
-
* Empty JSON array.
|
|
74
|
+
* Empty JSON array. Postgres only - the `::jsonb` cast is not portable.
|
|
107
75
|
*/
|
|
108
76
|
emptyArray() {
|
|
109
77
|
return new SqlExpression("'[]'::jsonb");
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* - MigrationBuilder: Execute DDL operations (for integration tests/runtime)
|
|
7
7
|
*/
|
|
8
8
|
import { normalizeIndexColumn } from '../../util/index.js';
|
|
9
|
+
import { derivedIndexName } from '../../util/sql.util.js';
|
|
9
10
|
import { createSchemaGenerator } from '../schemaGenerator.js';
|
|
10
11
|
import { splitSqlStatements } from './splitSqlStatements.js';
|
|
11
12
|
import { TableBuilder } from './tableBuilder.js';
|
|
@@ -26,7 +27,8 @@ function createIndexOperation(tableName, columns, options = {}) {
|
|
|
26
27
|
tableName,
|
|
27
28
|
index: {
|
|
28
29
|
...index,
|
|
29
|
-
name: name ??
|
|
30
|
+
name: name ??
|
|
31
|
+
derivedIndexName(tableName, entries.map((entry) => entry.column)),
|
|
30
32
|
entries,
|
|
31
33
|
unique: unique ?? false,
|
|
32
34
|
},
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* Fluent API for defining tables in migrations.
|
|
5
5
|
*/
|
|
6
6
|
import { normalizeIndexColumn } from '../../util/index.js';
|
|
7
|
+
import { derivedIndexName } from '../../util/sql.util.js';
|
|
7
8
|
import { ColumnBuilder } from './columnBuilder.js';
|
|
8
|
-
import {
|
|
9
|
+
import { expr } from './expressions.js';
|
|
9
10
|
/**
|
|
10
11
|
* Builder for table-level foreign keys.
|
|
11
12
|
*/
|
|
@@ -136,10 +137,10 @@ export class TableBuilder {
|
|
|
136
137
|
return column;
|
|
137
138
|
}
|
|
138
139
|
createdAt() {
|
|
139
|
-
return this.add('createdAt', { category: 'timestamp' }, { defaultValue:
|
|
140
|
+
return this.add('createdAt', { category: 'timestamp' }, { defaultValue: expr.now() });
|
|
140
141
|
}
|
|
141
142
|
updatedAt() {
|
|
142
|
-
return this.add('updatedAt', { category: 'timestamp' }, { defaultValue:
|
|
143
|
+
return this.add('updatedAt', { category: 'timestamp' }, { defaultValue: expr.now() });
|
|
143
144
|
}
|
|
144
145
|
timestamps() {
|
|
145
146
|
this.createdAt();
|
|
@@ -190,7 +191,7 @@ export class TableBuilder {
|
|
|
190
191
|
// Collect column-level indexes
|
|
191
192
|
for (const col of columns) {
|
|
192
193
|
if (col.index) {
|
|
193
|
-
const indexName = typeof col.index === 'string' ? col.index :
|
|
194
|
+
const indexName = typeof col.index === 'string' ? col.index : derivedIndexName(this._name, [col.name]);
|
|
194
195
|
// Only add if not already in table-level indexes
|
|
195
196
|
if (!this._indexes.some((idx) => idx.name === indexName)) {
|
|
196
197
|
this._indexes.push({
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { derivedForeignKeyName } from '../../util/sql.util.js';
|
|
1
2
|
/**
|
|
2
3
|
* A migration builder's table definition as the AST nodes the generators render from, so a hand-written
|
|
3
4
|
* `createTable` and an entity reach `generateCreateTableFromNode` in the same shape. Free functions and
|
|
@@ -11,7 +12,6 @@ export function tableDefinitionToNode(def) {
|
|
|
11
12
|
columns,
|
|
12
13
|
primaryKey: [], // placeholder
|
|
13
14
|
indexes: [],
|
|
14
|
-
schema: { tables: new Map(), relationships: [], indexes: [] },
|
|
15
15
|
incomingRelations: [],
|
|
16
16
|
outgoingRelations: [],
|
|
17
17
|
comment: def.comment,
|
|
@@ -33,7 +33,7 @@ export function tableDefinitionToNode(def) {
|
|
|
33
33
|
}
|
|
34
34
|
for (const fkDef of def.foreignKeys) {
|
|
35
35
|
const relNode = {
|
|
36
|
-
name: fkDef.name ??
|
|
36
|
+
name: fkDef.name ?? derivedForeignKeyName(def.name, fkDef.columns),
|
|
37
37
|
type: 'ManyToOne', // Builder default
|
|
38
38
|
from: {
|
|
39
39
|
table,
|
|
@@ -64,7 +64,7 @@ export function fullColumnDefinitionToNode(col, tableName) {
|
|
|
64
64
|
referencedBy: [],
|
|
65
65
|
references: col.foreignKey
|
|
66
66
|
? {
|
|
67
|
-
name:
|
|
67
|
+
name: derivedForeignKeyName(tableName, [col.name]),
|
|
68
68
|
type: 'ManyToOne',
|
|
69
69
|
from: { table: { name: tableName }, columns: [] },
|
|
70
70
|
to: {
|
|
@@ -2,6 +2,7 @@ import { AbstractDialect } from '../../dialect/abstractDialect.js';
|
|
|
2
2
|
import { getMeta } from '../../entity/index.js';
|
|
3
3
|
import { mongoDialectFeatures } from '../../mongo/mongoDialect.js';
|
|
4
4
|
import { getKeys } from '../../util/index.js';
|
|
5
|
+
import { derivedIndexName } from '../../util/sql.util.js';
|
|
5
6
|
import { indexNodeToSchema } from './indexNodeToSchema.js';
|
|
6
7
|
import { serializeMongoCommand } from './mongoCommand.js';
|
|
7
8
|
export class MongoSchemaGenerator extends AbstractDialect {
|
|
@@ -39,7 +40,7 @@ export class MongoSchemaGenerator extends AbstractDialect {
|
|
|
39
40
|
const field = meta.fields[key];
|
|
40
41
|
if (field?.index) {
|
|
41
42
|
const columnName = this.resolveColumnName(key, field);
|
|
42
|
-
const indexName = typeof field.index === 'string' ? field.index :
|
|
43
|
+
const indexName = typeof field.index === 'string' ? field.index : derivedIndexName(collectionName, [columnName]);
|
|
43
44
|
indexes.push({
|
|
44
45
|
name: indexName,
|
|
45
46
|
entries: [{ column: columnName }],
|
|
@@ -142,7 +143,7 @@ export class MongoSchemaGenerator extends AbstractDialect {
|
|
|
142
143
|
const field = meta.fields[key];
|
|
143
144
|
if (field?.index) {
|
|
144
145
|
const columnName = this.resolveColumnName(key, field);
|
|
145
|
-
const indexName = typeof field.index === 'string' ? field.index :
|
|
146
|
+
const indexName = typeof field.index === 'string' ? field.index : derivedIndexName(collectionName, [columnName]);
|
|
146
147
|
if (!existingIndexes.has(indexName)) {
|
|
147
148
|
indexesToAdd.push({
|
|
148
149
|
name: indexName,
|
|
@@ -36,7 +36,18 @@ export type TableRowReader = <T extends RawRow>(sql: string, params?: unknown[])
|
|
|
36
36
|
*/
|
|
37
37
|
export declare abstract class AbstractSqlSchemaIntrospector extends BaseSqlIntrospector implements SchemaIntrospector {
|
|
38
38
|
protected readonly pool: QuerierPool;
|
|
39
|
-
constructor(pool: QuerierPool);
|
|
39
|
+
constructor(pool: QuerierPool, schema?: string);
|
|
40
|
+
/**
|
|
41
|
+
* The schema every catalogue query filters on, as SQL: the one that was asked for, or the engine's
|
|
42
|
+
* expression for the connection's default. A literal rather than a bind parameter because these
|
|
43
|
+
* queries are assembled as text and several use it more than once.
|
|
44
|
+
*/
|
|
45
|
+
protected get schemaExpr(): string;
|
|
46
|
+
/**
|
|
47
|
+
* How this engine names the connection's current schema (Postgres) or database (MySQL). Empty on
|
|
48
|
+
* an engine with no schemas, whose catalogue queries never reference one.
|
|
49
|
+
*/
|
|
50
|
+
protected readonly defaultSchemaExpr: string;
|
|
40
51
|
getTableSchema(tableName: string): Promise<TableSchema | undefined>;
|
|
41
52
|
getTableNames(): Promise<string[]>;
|
|
42
53
|
tableExists(tableName: string): Promise<boolean>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isSqlQuerier } from '../../type/index.js';
|
|
2
|
+
import { escapeAnsiSqlLiteral } from '../../util/sqlLiteral.js';
|
|
2
3
|
import { BaseSqlIntrospector } from './baseSqlIntrospector.js';
|
|
3
4
|
/**
|
|
4
5
|
* Abstract base class for SQL schema introspectors.
|
|
@@ -21,10 +22,23 @@ import { BaseSqlIntrospector } from './baseSqlIntrospector.js';
|
|
|
21
22
|
*/
|
|
22
23
|
export class AbstractSqlSchemaIntrospector extends BaseSqlIntrospector {
|
|
23
24
|
pool;
|
|
24
|
-
constructor(pool) {
|
|
25
|
-
super(pool.dialect);
|
|
25
|
+
constructor(pool, schema) {
|
|
26
|
+
super(pool.dialect, schema);
|
|
26
27
|
this.pool = pool;
|
|
27
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* The schema every catalogue query filters on, as SQL: the one that was asked for, or the engine's
|
|
31
|
+
* expression for the connection's default. A literal rather than a bind parameter because these
|
|
32
|
+
* queries are assembled as text and several use it more than once.
|
|
33
|
+
*/
|
|
34
|
+
get schemaExpr() {
|
|
35
|
+
return this.schema === undefined ? this.defaultSchemaExpr : escapeAnsiSqlLiteral(this.schema);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* How this engine names the connection's current schema (Postgres) or database (MySQL). Empty on
|
|
39
|
+
* an engine with no schemas, whose catalogue queries never reference one.
|
|
40
|
+
*/
|
|
41
|
+
defaultSchemaExpr = '';
|
|
28
42
|
async getTableSchema(tableName) {
|
|
29
43
|
return this.withSqlQuerier(async (querier) => {
|
|
30
44
|
const read = createTableRowReader(querier);
|
|
@@ -7,9 +7,15 @@ import type { TableSchema } from '../../type/migration.js';
|
|
|
7
7
|
*/
|
|
8
8
|
export declare abstract class BaseSqlIntrospector {
|
|
9
9
|
protected readonly dialect: AbstractSqlDialect;
|
|
10
|
+
readonly schema?: string | undefined;
|
|
10
11
|
/** Columns and uniqueness only; each introspector opts in to what its catalogue queries report. */
|
|
11
12
|
readonly indexFacets: ReadonlySet<IndexFacet>;
|
|
12
|
-
|
|
13
|
+
/**
|
|
14
|
+
* The one schema these queries read, `undefined` for the connection's own default. Every table
|
|
15
|
+
* reported is stamped with it, so a diff compares like with like: entity and database both say
|
|
16
|
+
* `undefined` for "wherever the connection points", and name a schema only when one was asked for.
|
|
17
|
+
*/
|
|
18
|
+
constructor(dialect: AbstractSqlDialect, schema?: string | undefined);
|
|
13
19
|
protected escapeId(identifier: string): string;
|
|
14
20
|
/**
|
|
15
21
|
* Introspect entire database schema and return SchemaAST.
|
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import { canonicalColumnType } from '../../schema/canonicalType.js';
|
|
2
|
-
import { SchemaAST } from '../../schema/schemaAST.js';
|
|
2
|
+
import { createTableNode, SchemaAST } from '../../schema/schemaAST.js';
|
|
3
3
|
import { escapeSqlId } from '../../util/index.js';
|
|
4
4
|
/**
|
|
5
5
|
* Base class for SQL introspectors with shared AST building logic.
|
|
6
6
|
*/
|
|
7
7
|
export class BaseSqlIntrospector {
|
|
8
8
|
dialect;
|
|
9
|
+
schema;
|
|
9
10
|
/** Columns and uniqueness only; each introspector opts in to what its catalogue queries report. */
|
|
10
11
|
indexFacets = new Set();
|
|
11
|
-
|
|
12
|
+
/**
|
|
13
|
+
* The one schema these queries read, `undefined` for the connection's own default. Every table
|
|
14
|
+
* reported is stamped with it, so a diff compares like with like: entity and database both say
|
|
15
|
+
* `undefined` for "wherever the connection points", and name a schema only when one was asked for.
|
|
16
|
+
*/
|
|
17
|
+
constructor(dialect, schema) {
|
|
12
18
|
this.dialect = dialect;
|
|
19
|
+
this.schema = schema;
|
|
13
20
|
}
|
|
14
21
|
escapeId(identifier) {
|
|
15
22
|
return escapeSqlId(identifier, this.dialect.escapeIdChar);
|
|
@@ -41,16 +48,8 @@ export class BaseSqlIntrospector {
|
|
|
41
48
|
}
|
|
42
49
|
buildTables(ast, tableNodes, tableSchemas) {
|
|
43
50
|
for (const schema of tableSchemas) {
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
name: schema.name,
|
|
47
|
-
columns,
|
|
48
|
-
primaryKey: [],
|
|
49
|
-
indexes: [],
|
|
50
|
-
schema: ast,
|
|
51
|
-
incomingRelations: [],
|
|
52
|
-
outgoingRelations: [],
|
|
53
|
-
};
|
|
51
|
+
const table = createTableNode(schema.name, this.schema);
|
|
52
|
+
const { columns } = table;
|
|
54
53
|
for (const col of schema.columns) {
|
|
55
54
|
const column = {
|
|
56
55
|
name: col.name,
|
|
@@ -5,6 +5,7 @@ import { AbstractSqlSchemaIntrospector, type TableRowReader } from './abstractSq
|
|
|
5
5
|
* Works with both MySQL and MariaDB as they share the same information_schema structure.
|
|
6
6
|
*/
|
|
7
7
|
export declare class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
8
|
+
protected readonly defaultSchemaExpr = "DATABASE()";
|
|
8
9
|
protected getTableNamesQuery(): string;
|
|
9
10
|
protected tableExistsQuery(): string;
|
|
10
11
|
protected parseTableExistsResult(results: {
|
|
@@ -4,11 +4,13 @@ import { AbstractSqlSchemaIntrospector } from './abstractSqlSchemaIntrospector.j
|
|
|
4
4
|
* Works with both MySQL and MariaDB as they share the same information_schema structure.
|
|
5
5
|
*/
|
|
6
6
|
export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
7
|
+
// A MySQL "schema" is a database, so the connection's own is what `DATABASE()` reports.
|
|
8
|
+
defaultSchemaExpr = 'DATABASE()';
|
|
7
9
|
getTableNamesQuery() {
|
|
8
10
|
return /*sql*/ `
|
|
9
11
|
SELECT TABLE_NAME as table_name
|
|
10
12
|
FROM information_schema.TABLES
|
|
11
|
-
WHERE TABLE_SCHEMA =
|
|
13
|
+
WHERE TABLE_SCHEMA = ${this.schemaExpr}
|
|
12
14
|
AND TABLE_TYPE = 'BASE TABLE'
|
|
13
15
|
ORDER BY TABLE_NAME
|
|
14
16
|
`;
|
|
@@ -17,7 +19,7 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
17
19
|
return /*sql*/ `
|
|
18
20
|
SELECT COUNT(*) as count
|
|
19
21
|
FROM information_schema.TABLES
|
|
20
|
-
WHERE TABLE_SCHEMA =
|
|
22
|
+
WHERE TABLE_SCHEMA = ${this.schemaExpr}
|
|
21
23
|
AND TABLE_NAME = ?
|
|
22
24
|
`;
|
|
23
25
|
}
|
|
@@ -43,7 +45,7 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
43
45
|
EXTRA as extra,
|
|
44
46
|
COLUMN_COMMENT as column_comment
|
|
45
47
|
FROM information_schema.COLUMNS
|
|
46
|
-
WHERE TABLE_SCHEMA =
|
|
48
|
+
WHERE TABLE_SCHEMA = ${this.schemaExpr}
|
|
47
49
|
AND TABLE_NAME = ?
|
|
48
50
|
ORDER BY ORDINAL_POSITION
|
|
49
51
|
`;
|
|
@@ -55,7 +57,7 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
55
57
|
GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX) as columns,
|
|
56
58
|
NOT NON_UNIQUE as is_unique
|
|
57
59
|
FROM information_schema.STATISTICS
|
|
58
|
-
WHERE TABLE_SCHEMA =
|
|
60
|
+
WHERE TABLE_SCHEMA = ${this.schemaExpr}
|
|
59
61
|
AND TABLE_NAME = ?
|
|
60
62
|
AND INDEX_NAME != 'PRIMARY'
|
|
61
63
|
GROUP BY INDEX_NAME, NON_UNIQUE
|
|
@@ -75,7 +77,7 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
75
77
|
JOIN information_schema.REFERENTIAL_CONSTRAINTS rc
|
|
76
78
|
ON kcu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
|
|
77
79
|
AND kcu.TABLE_SCHEMA = rc.CONSTRAINT_SCHEMA
|
|
78
|
-
WHERE kcu.TABLE_SCHEMA =
|
|
80
|
+
WHERE kcu.TABLE_SCHEMA = ${this.schemaExpr}
|
|
79
81
|
AND kcu.TABLE_NAME = ?
|
|
80
82
|
AND kcu.REFERENCED_TABLE_NAME IS NOT NULL
|
|
81
83
|
GROUP BY kcu.CONSTRAINT_NAME, kcu.REFERENCED_TABLE_NAME, rc.DELETE_RULE, rc.UPDATE_RULE
|
|
@@ -86,7 +88,7 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
86
88
|
return /*sql*/ `
|
|
87
89
|
SELECT COLUMN_NAME as column_name
|
|
88
90
|
FROM information_schema.KEY_COLUMN_USAGE
|
|
89
|
-
WHERE TABLE_SCHEMA =
|
|
91
|
+
WHERE TABLE_SCHEMA = ${this.schemaExpr}
|
|
90
92
|
AND TABLE_NAME = ?
|
|
91
93
|
AND CONSTRAINT_NAME = 'PRIMARY'
|
|
92
94
|
ORDER BY ORDINAL_POSITION
|
|
@@ -5,6 +5,7 @@ import { AbstractSqlSchemaIntrospector, type TableRowReader } from './abstractSq
|
|
|
5
5
|
* PostgreSQL schema introspector
|
|
6
6
|
*/
|
|
7
7
|
export declare class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
8
|
+
protected readonly defaultSchemaExpr = "current_schema()";
|
|
8
9
|
/**
|
|
9
10
|
* Expressions and predicates are read back too, for `generate:from-db`, but they are text the
|
|
10
11
|
* database reprints in its own words, so they are not comparable and are not claimed here.
|
|
@@ -4,6 +4,7 @@ import { AbstractSqlSchemaIntrospector } from './abstractSqlSchemaIntrospector.j
|
|
|
4
4
|
* PostgreSQL schema introspector
|
|
5
5
|
*/
|
|
6
6
|
export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
7
|
+
defaultSchemaExpr = 'current_schema()';
|
|
7
8
|
/**
|
|
8
9
|
* Expressions and predicates are read back too, for `generate:from-db`, but they are text the
|
|
9
10
|
* database reprints in its own words, so they are not comparable and are not claimed here.
|
|
@@ -19,7 +20,7 @@ export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
19
20
|
return /*sql*/ `
|
|
20
21
|
SELECT table_name
|
|
21
22
|
FROM information_schema.tables
|
|
22
|
-
WHERE table_schema =
|
|
23
|
+
WHERE table_schema = ${this.schemaExpr}
|
|
23
24
|
AND table_type = 'BASE TABLE'
|
|
24
25
|
ORDER BY table_name
|
|
25
26
|
`;
|
|
@@ -28,7 +29,7 @@ export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
28
29
|
return /*sql*/ `
|
|
29
30
|
SELECT EXISTS (
|
|
30
31
|
SELECT FROM information_schema.tables
|
|
31
|
-
WHERE table_schema =
|
|
32
|
+
WHERE table_schema = ${this.schemaExpr}
|
|
32
33
|
AND table_name = $1
|
|
33
34
|
) AS exists
|
|
34
35
|
`;
|
|
@@ -74,7 +75,7 @@ export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
74
75
|
c.ordinal_position
|
|
75
76
|
) AS column_comment
|
|
76
77
|
FROM information_schema.columns c
|
|
77
|
-
WHERE c.table_schema =
|
|
78
|
+
WHERE c.table_schema = ${this.schemaExpr}
|
|
78
79
|
AND c.table_name = $1
|
|
79
80
|
ORDER BY c.ordinal_position
|
|
80
81
|
`;
|
|
@@ -152,7 +153,7 @@ export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
152
153
|
AND rc.constraint_schema = tc.table_schema
|
|
153
154
|
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|
154
155
|
AND tc.table_name = $1
|
|
155
|
-
AND tc.table_schema =
|
|
156
|
+
AND tc.table_schema = ${this.schemaExpr}
|
|
156
157
|
GROUP BY tc.constraint_name, ccu.table_name, rc.delete_rule, rc.update_rule
|
|
157
158
|
ORDER BY tc.constraint_name
|
|
158
159
|
`;
|
|
@@ -166,7 +167,7 @@ export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
166
167
|
AND tc.table_schema = kcu.table_schema
|
|
167
168
|
WHERE tc.constraint_type = 'PRIMARY KEY'
|
|
168
169
|
AND tc.table_name = $1
|
|
169
|
-
AND tc.table_schema =
|
|
170
|
+
AND tc.table_schema = ${this.schemaExpr}
|
|
170
171
|
ORDER BY kcu.ordinal_position
|
|
171
172
|
`;
|
|
172
173
|
}
|
|
@@ -25,7 +25,8 @@ export declare class Migrator {
|
|
|
25
25
|
* Set the schema generator for DDL operations
|
|
26
26
|
*/
|
|
27
27
|
setSchemaGenerator(generator: SchemaGenerator): void;
|
|
28
|
-
|
|
28
|
+
/** `schema` reads one namespace instead of the connection's own; see {@link BaseSqlIntrospector.schema}. */
|
|
29
|
+
protected createIntrospector(schema?: string): SchemaIntrospector | undefined;
|
|
29
30
|
protected createGenerator(): SchemaGenerator | undefined;
|
|
30
31
|
/**
|
|
31
32
|
* Get all discovered migrations from the migrations directory
|
|
@@ -77,6 +78,14 @@ export declare class Migrator {
|
|
|
77
78
|
* Get all schema differences between entities and database
|
|
78
79
|
*/
|
|
79
80
|
getDiffs(): Promise<SchemaDiff[]>;
|
|
81
|
+
/**
|
|
82
|
+
* One AST spanning every schema the entities claim, each table stamped with the schema it was read
|
|
83
|
+
* from. Read per schema rather than all at once, so a table comes back keyed exactly as the entity
|
|
84
|
+
* that wants it spells the key: `undefined` on both sides for the connection's default, a name on
|
|
85
|
+
* both sides otherwise. Ordinarily that is one schema and one pass, as before, and that pass keeps
|
|
86
|
+
* {@link schemaIntrospector} so a caller that replaced it still wins.
|
|
87
|
+
*/
|
|
88
|
+
private introspectClaimedSchemas;
|
|
80
89
|
findEntityForTable(tableName: string): Promise<Type<unknown> | undefined>;
|
|
81
90
|
/**
|
|
82
91
|
* Sync schema directly (for development only - not for production!)
|
|
@@ -189,10 +198,10 @@ export interface BuilderMigrationDefinition {
|
|
|
189
198
|
* ```ts
|
|
190
199
|
* export default defineBuilderMigration({
|
|
191
200
|
* async up(m) {
|
|
192
|
-
* await m.createTable('users', (
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
201
|
+
* await m.createTable('users', (t) => {
|
|
202
|
+
* t.id();
|
|
203
|
+
* t.string('email', { length: 255 }).unique();
|
|
204
|
+
* t.timestamps();
|
|
196
205
|
* });
|
|
197
206
|
* },
|
|
198
207
|
* async down(m) {
|