@mikro-orm/sql 7.0.0-dev.98 → 7.0.0-rc.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/AbstractSqlConnection.d.ts +6 -7
- package/AbstractSqlConnection.js +27 -24
- package/AbstractSqlDriver.d.ts +82 -23
- package/AbstractSqlDriver.js +584 -184
- package/AbstractSqlPlatform.d.ts +3 -4
- package/AbstractSqlPlatform.js +0 -4
- package/PivotCollectionPersister.d.ts +5 -0
- package/PivotCollectionPersister.js +30 -12
- package/SqlEntityManager.d.ts +2 -2
- package/dialects/mysql/{MySqlPlatform.d.ts → BaseMySqlPlatform.d.ts} +3 -2
- package/dialects/mysql/{MySqlPlatform.js → BaseMySqlPlatform.js} +5 -1
- package/dialects/mysql/MySqlSchemaHelper.d.ts +12 -1
- package/dialects/mysql/MySqlSchemaHelper.js +97 -6
- package/dialects/mysql/index.d.ts +1 -2
- package/dialects/mysql/index.js +1 -2
- package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +106 -0
- package/dialects/postgresql/BasePostgreSqlPlatform.js +350 -0
- package/dialects/postgresql/FullTextType.d.ts +14 -0
- package/dialects/postgresql/FullTextType.js +59 -0
- package/dialects/postgresql/PostgreSqlExceptionConverter.d.ts +8 -0
- package/dialects/postgresql/PostgreSqlExceptionConverter.js +47 -0
- package/dialects/postgresql/PostgreSqlSchemaHelper.d.ts +90 -0
- package/dialects/postgresql/PostgreSqlSchemaHelper.js +732 -0
- package/dialects/postgresql/index.d.ts +3 -0
- package/dialects/postgresql/index.js +3 -0
- package/dialects/sqlite/BaseSqliteConnection.d.ts +1 -0
- package/dialects/sqlite/BaseSqliteConnection.js +14 -1
- package/dialects/sqlite/BaseSqlitePlatform.d.ts +6 -0
- package/dialects/sqlite/BaseSqlitePlatform.js +12 -0
- package/dialects/sqlite/SqliteSchemaHelper.d.ts +25 -0
- package/dialects/sqlite/SqliteSchemaHelper.js +145 -19
- package/dialects/sqlite/index.d.ts +0 -1
- package/dialects/sqlite/index.js +0 -1
- package/package.json +5 -6
- package/plugin/transformer.d.ts +1 -1
- package/plugin/transformer.js +1 -1
- package/query/CriteriaNode.d.ts +9 -5
- package/query/CriteriaNode.js +16 -15
- package/query/CriteriaNodeFactory.d.ts +6 -6
- package/query/CriteriaNodeFactory.js +33 -31
- package/query/NativeQueryBuilder.d.ts +3 -2
- package/query/NativeQueryBuilder.js +1 -2
- package/query/ObjectCriteriaNode.js +50 -35
- package/query/QueryBuilder.d.ts +548 -79
- package/query/QueryBuilder.js +537 -159
- package/query/QueryBuilderHelper.d.ts +22 -14
- package/query/QueryBuilderHelper.js +158 -69
- package/query/ScalarCriteriaNode.js +2 -2
- package/query/raw.d.ts +11 -3
- package/query/raw.js +1 -2
- package/schema/DatabaseSchema.d.ts +15 -2
- package/schema/DatabaseSchema.js +143 -15
- package/schema/DatabaseTable.d.ts +12 -0
- package/schema/DatabaseTable.js +91 -31
- package/schema/SchemaComparator.d.ts +8 -0
- package/schema/SchemaComparator.js +126 -3
- package/schema/SchemaHelper.d.ts +26 -3
- package/schema/SchemaHelper.js +98 -11
- package/schema/SqlSchemaGenerator.d.ts +10 -0
- package/schema/SqlSchemaGenerator.js +137 -9
- package/tsconfig.build.tsbuildinfo +1 -0
- package/typings.d.ts +74 -36
- package/dialects/postgresql/PostgreSqlTableCompiler.d.ts +0 -1
- package/dialects/postgresql/PostgreSqlTableCompiler.js +0 -1
|
@@ -3,6 +3,19 @@ import { AbstractSqlConnection } from '../../AbstractSqlConnection.js';
|
|
|
3
3
|
export class BaseSqliteConnection extends AbstractSqlConnection {
|
|
4
4
|
async connect(options) {
|
|
5
5
|
await super.connect(options);
|
|
6
|
-
await this.
|
|
6
|
+
await this.getClient().executeQuery(CompiledQuery.raw('pragma foreign_keys = on'));
|
|
7
|
+
await this.attachDatabases();
|
|
8
|
+
}
|
|
9
|
+
async attachDatabases() {
|
|
10
|
+
const attachDatabases = this.config.get('attachDatabases');
|
|
11
|
+
if (!attachDatabases?.length) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const { fs } = await import('@mikro-orm/core/fs-utils');
|
|
15
|
+
const baseDir = this.config.get('baseDir');
|
|
16
|
+
for (const db of attachDatabases) {
|
|
17
|
+
const path = fs.absolutePath(db.path, baseDir);
|
|
18
|
+
await this.execute(`attach database '${path}' as ${this.platform.quoteIdentifier(db.name)}`);
|
|
19
|
+
}
|
|
7
20
|
}
|
|
8
21
|
}
|
|
@@ -64,6 +64,12 @@ export declare abstract class BaseSqlitePlatform extends AbstractSqlPlatform {
|
|
|
64
64
|
processDateProperty(value: unknown): string | number | Date;
|
|
65
65
|
getIndexName(tableName: string, columns: string[], type: 'index' | 'unique' | 'foreign' | 'primary' | 'sequence'): string;
|
|
66
66
|
supportsDeferredUniqueConstraints(): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* SQLite supports schemas via ATTACH DATABASE. Returns true when there are
|
|
69
|
+
* attached databases configured.
|
|
70
|
+
*/
|
|
71
|
+
supportsSchemas(): boolean;
|
|
72
|
+
getDefaultSchemaName(): string | undefined;
|
|
67
73
|
getFullTextWhereClause(): string;
|
|
68
74
|
quoteVersionValue(value: Date | number, prop: EntityProperty): Date | string | number;
|
|
69
75
|
quoteValue(value: any): string;
|
|
@@ -86,6 +86,18 @@ export class BaseSqlitePlatform extends AbstractSqlPlatform {
|
|
|
86
86
|
supportsDeferredUniqueConstraints() {
|
|
87
87
|
return false;
|
|
88
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* SQLite supports schemas via ATTACH DATABASE. Returns true when there are
|
|
91
|
+
* attached databases configured.
|
|
92
|
+
*/
|
|
93
|
+
supportsSchemas() {
|
|
94
|
+
const attachDatabases = this.config.get('attachDatabases');
|
|
95
|
+
return !!attachDatabases?.length;
|
|
96
|
+
}
|
|
97
|
+
getDefaultSchemaName() {
|
|
98
|
+
// Return 'main' only when schema support is active (i.e., databases are attached)
|
|
99
|
+
return this.supportsSchemas() ? 'main' : undefined;
|
|
100
|
+
}
|
|
89
101
|
getFullTextWhereClause() {
|
|
90
102
|
return `:column: match :query`;
|
|
91
103
|
}
|
|
@@ -8,7 +8,14 @@ export declare class SqliteSchemaHelper extends SchemaHelper {
|
|
|
8
8
|
disableForeignKeysSQL(): string;
|
|
9
9
|
enableForeignKeysSQL(): string;
|
|
10
10
|
supportsSchemaConstraints(): boolean;
|
|
11
|
+
getCreateNamespaceSQL(name: string): string;
|
|
12
|
+
getDropNamespaceSQL(name: string): string;
|
|
11
13
|
getListTablesSQL(): string;
|
|
14
|
+
getAllTables(connection: AbstractSqlConnection, schemas?: string[]): Promise<Table[]>;
|
|
15
|
+
getNamespaces(connection: AbstractSqlConnection): Promise<string[]>;
|
|
16
|
+
private getIgnoredViewsCondition;
|
|
17
|
+
getListViewsSQL(): string;
|
|
18
|
+
loadViews(schema: DatabaseSchema, connection: AbstractSqlConnection, schemaName?: string): Promise<void>;
|
|
12
19
|
getDropDatabaseSQL(name: string): string;
|
|
13
20
|
loadInformationSchema(schema: DatabaseSchema, connection: AbstractSqlConnection, tables: Table[], schemas?: string[]): Promise<void>;
|
|
14
21
|
createTable(table: DatabaseTable, alter?: boolean): string[];
|
|
@@ -18,6 +25,19 @@ export declare class SqliteSchemaHelper extends SchemaHelper {
|
|
|
18
25
|
getDropColumnsSQL(tableName: string, columns: Column[], schemaName?: string): string;
|
|
19
26
|
getCreateIndexSQL(tableName: string, index: IndexDef): string;
|
|
20
27
|
private parseTableDefinition;
|
|
28
|
+
/**
|
|
29
|
+
* Returns schema prefix for pragma and sqlite_master queries.
|
|
30
|
+
* Returns empty string for main database (no prefix needed).
|
|
31
|
+
*/
|
|
32
|
+
private getSchemaPrefix;
|
|
33
|
+
/**
|
|
34
|
+
* Returns all database names excluding 'temp'.
|
|
35
|
+
*/
|
|
36
|
+
private getDatabaseList;
|
|
37
|
+
/**
|
|
38
|
+
* Extracts the SELECT part from a CREATE VIEW statement.
|
|
39
|
+
*/
|
|
40
|
+
private extractViewDefinition;
|
|
21
41
|
private getColumns;
|
|
22
42
|
private getEnumDefinitions;
|
|
23
43
|
getPrimaryKeys(connection: AbstractSqlConnection, indexes: IndexDef[], tableName: string, schemaName?: string): Promise<string[]>;
|
|
@@ -33,6 +53,11 @@ export declare class SqliteSchemaHelper extends SchemaHelper {
|
|
|
33
53
|
*/
|
|
34
54
|
isImplicitIndex(name: string): boolean;
|
|
35
55
|
dropIndex(table: string, index: IndexDef, oldIndexName?: string): string;
|
|
56
|
+
/**
|
|
57
|
+
* SQLite does not support schema-qualified table names in REFERENCES clauses.
|
|
58
|
+
* Foreign key references can only point to tables in the same database.
|
|
59
|
+
*/
|
|
60
|
+
getReferencedTableName(referencedTableName: string, schema?: string): string;
|
|
36
61
|
alterTable(diff: TableDifference, safe?: boolean): string[];
|
|
37
62
|
private getAlterTempTableSQL;
|
|
38
63
|
}
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { Utils } from '@mikro-orm/core';
|
|
2
2
|
import { SchemaHelper } from '../../schema/SchemaHelper.js';
|
|
3
|
+
/** SpatiaLite system views that should be automatically ignored */
|
|
4
|
+
const SPATIALITE_VIEWS = [
|
|
5
|
+
'geometry_columns',
|
|
6
|
+
'spatial_ref_sys',
|
|
7
|
+
'views_geometry_columns',
|
|
8
|
+
'virts_geometry_columns',
|
|
9
|
+
'geom_cols_ref_sys',
|
|
10
|
+
'spatial_ref_sys_aux',
|
|
11
|
+
'vector_layers',
|
|
12
|
+
'vector_layers_auth',
|
|
13
|
+
'vector_layers_field_infos',
|
|
14
|
+
'vector_layers_statistics',
|
|
15
|
+
'ElementaryGeometries',
|
|
16
|
+
];
|
|
3
17
|
export class SqliteSchemaHelper extends SchemaHelper {
|
|
4
18
|
disableForeignKeysSQL() {
|
|
5
19
|
return 'pragma foreign_keys = off;';
|
|
@@ -10,10 +24,67 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
10
24
|
supportsSchemaConstraints() {
|
|
11
25
|
return false;
|
|
12
26
|
}
|
|
27
|
+
getCreateNamespaceSQL(name) {
|
|
28
|
+
return '';
|
|
29
|
+
}
|
|
30
|
+
getDropNamespaceSQL(name) {
|
|
31
|
+
return '';
|
|
32
|
+
}
|
|
13
33
|
getListTablesSQL() {
|
|
14
34
|
return `select name as table_name from sqlite_master where type = 'table' and name != 'sqlite_sequence' and name != 'geometry_columns' and name != 'spatial_ref_sys' `
|
|
15
35
|
+ `union all select name as table_name from sqlite_temp_master where type = 'table' order by name`;
|
|
16
36
|
}
|
|
37
|
+
async getAllTables(connection, schemas) {
|
|
38
|
+
const databases = await this.getDatabaseList(connection);
|
|
39
|
+
const hasAttachedDbs = databases.length > 1; // More than just 'main'
|
|
40
|
+
// If no attached databases, use original behavior
|
|
41
|
+
if (!hasAttachedDbs && !schemas?.length) {
|
|
42
|
+
return connection.execute(this.getListTablesSQL());
|
|
43
|
+
}
|
|
44
|
+
// With attached databases, query each one
|
|
45
|
+
const targetSchemas = schemas?.length ? schemas : databases;
|
|
46
|
+
const allTables = [];
|
|
47
|
+
for (const dbName of targetSchemas) {
|
|
48
|
+
const prefix = this.getSchemaPrefix(dbName);
|
|
49
|
+
const tables = await connection.execute(`select name from ${prefix}sqlite_master where type = 'table' ` +
|
|
50
|
+
`and name != 'sqlite_sequence' and name != 'geometry_columns' and name != 'spatial_ref_sys'`);
|
|
51
|
+
for (const t of tables) {
|
|
52
|
+
allTables.push({ table_name: t.name, schema_name: dbName });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return allTables;
|
|
56
|
+
}
|
|
57
|
+
async getNamespaces(connection) {
|
|
58
|
+
return this.getDatabaseList(connection);
|
|
59
|
+
}
|
|
60
|
+
getIgnoredViewsCondition() {
|
|
61
|
+
return SPATIALITE_VIEWS.map(v => `name != '${v}'`).join(' and ');
|
|
62
|
+
}
|
|
63
|
+
getListViewsSQL() {
|
|
64
|
+
return `select name as view_name, sql as view_definition from sqlite_master where type = 'view' and ${this.getIgnoredViewsCondition()} order by name`;
|
|
65
|
+
}
|
|
66
|
+
async loadViews(schema, connection, schemaName) {
|
|
67
|
+
const databases = await this.getDatabaseList(connection);
|
|
68
|
+
const hasAttachedDbs = databases.length > 1; // More than just 'main'
|
|
69
|
+
// If no attached databases and no specific schema, use original behavior
|
|
70
|
+
if (!hasAttachedDbs && !schemaName) {
|
|
71
|
+
const views = await connection.execute(this.getListViewsSQL());
|
|
72
|
+
for (const view of views) {
|
|
73
|
+
schema.addView(view.view_name, schemaName, this.extractViewDefinition(view.view_definition));
|
|
74
|
+
}
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
// With attached databases, query each one
|
|
78
|
+
/* v8 ignore next - schemaName branch not commonly used */
|
|
79
|
+
const targetDbs = schemaName ? [schemaName] : databases;
|
|
80
|
+
for (const dbName of targetDbs) {
|
|
81
|
+
const prefix = this.getSchemaPrefix(dbName);
|
|
82
|
+
const views = await connection.execute(`select name as view_name, sql as view_definition from ${prefix}sqlite_master where type = 'view' and ${this.getIgnoredViewsCondition()} order by name`);
|
|
83
|
+
for (const view of views) {
|
|
84
|
+
schema.addView(view.view_name, dbName, this.extractViewDefinition(view.view_definition));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
17
88
|
getDropDatabaseSQL(name) {
|
|
18
89
|
if (name === ':memory:') {
|
|
19
90
|
return '';
|
|
@@ -29,7 +100,7 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
29
100
|
const checks = await this.getChecks(connection, table.name, table.schema);
|
|
30
101
|
const pks = await this.getPrimaryKeys(connection, indexes, table.name, table.schema);
|
|
31
102
|
const fks = await this.getForeignKeys(connection, table.name, table.schema);
|
|
32
|
-
const enums = await this.getEnumDefinitions(connection, table.name);
|
|
103
|
+
const enums = await this.getEnumDefinitions(connection, table.name, table.schema);
|
|
33
104
|
table.init(cols, indexes, checks, pks, fks, enums);
|
|
34
105
|
}
|
|
35
106
|
}
|
|
@@ -61,6 +132,9 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
61
132
|
sql += ', ' + parts.join(', ');
|
|
62
133
|
}
|
|
63
134
|
sql += ')';
|
|
135
|
+
if (table.comment) {
|
|
136
|
+
sql += ` /* ${table.comment} */`;
|
|
137
|
+
}
|
|
64
138
|
const ret = [];
|
|
65
139
|
this.append(ret, sql);
|
|
66
140
|
for (const index of table.getIndexes()) {
|
|
@@ -118,16 +192,27 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
118
192
|
if (index.expression) {
|
|
119
193
|
return index.expression;
|
|
120
194
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const
|
|
195
|
+
// SQLite requires: CREATE INDEX schema.index_name ON table_name (columns)
|
|
196
|
+
// NOT: CREATE INDEX index_name ON schema.table_name (columns)
|
|
197
|
+
const [schemaName, rawTableName] = this.splitTableName(tableName);
|
|
198
|
+
const quotedTableName = this.quote(rawTableName);
|
|
199
|
+
// If there's a schema, prefix the index name with it
|
|
200
|
+
let keyName;
|
|
201
|
+
if (schemaName && schemaName !== 'main') {
|
|
202
|
+
keyName = `${this.quote(schemaName)}.${this.quote(index.keyName)}`;
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
keyName = this.quote(index.keyName);
|
|
206
|
+
}
|
|
207
|
+
const sqlPrefix = `create ${index.unique ? 'unique ' : ''}index ${keyName} on ${quotedTableName}`;
|
|
208
|
+
/* v8 ignore next 4 */
|
|
124
209
|
if (index.columnNames.some(column => column.includes('.'))) {
|
|
125
210
|
// JSON columns can have unique index but not unique constraint, and we need to distinguish those, so we can properly drop them
|
|
126
|
-
const sql = `create ${index.unique ? 'unique ' : ''}index ${keyName} on ${tableName} `;
|
|
127
211
|
const columns = this.platform.getJsonIndexDefinition(index);
|
|
128
|
-
return `${
|
|
212
|
+
return `${sqlPrefix} (${columns.join(', ')})`;
|
|
129
213
|
}
|
|
130
|
-
|
|
214
|
+
// Use getIndexColumns to support advanced options like sort order and collation
|
|
215
|
+
return `${sqlPrefix} (${this.getIndexColumns(index)})`;
|
|
131
216
|
}
|
|
132
217
|
parseTableDefinition(sql, cols) {
|
|
133
218
|
const columns = {};
|
|
@@ -152,9 +237,35 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
152
237
|
}
|
|
153
238
|
return { columns, constraints };
|
|
154
239
|
}
|
|
240
|
+
/**
|
|
241
|
+
* Returns schema prefix for pragma and sqlite_master queries.
|
|
242
|
+
* Returns empty string for main database (no prefix needed).
|
|
243
|
+
*/
|
|
244
|
+
getSchemaPrefix(schemaName) {
|
|
245
|
+
if (!schemaName || schemaName === 'main') {
|
|
246
|
+
return '';
|
|
247
|
+
}
|
|
248
|
+
return `${this.platform.quoteIdentifier(schemaName)}.`;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Returns all database names excluding 'temp'.
|
|
252
|
+
*/
|
|
253
|
+
async getDatabaseList(connection) {
|
|
254
|
+
const databases = await connection.execute('pragma database_list');
|
|
255
|
+
return databases.filter(d => d.name !== 'temp').map(d => d.name);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Extracts the SELECT part from a CREATE VIEW statement.
|
|
259
|
+
*/
|
|
260
|
+
extractViewDefinition(viewDefinition) {
|
|
261
|
+
const match = viewDefinition?.match(/create\s+view\s+[`"']?\w+[`"']?\s+as\s+(.*)/i);
|
|
262
|
+
/* v8 ignore next - fallback for non-standard view definitions */
|
|
263
|
+
return match ? match[1] : viewDefinition;
|
|
264
|
+
}
|
|
155
265
|
async getColumns(connection, tableName, schemaName) {
|
|
156
|
-
const
|
|
157
|
-
const
|
|
266
|
+
const prefix = this.getSchemaPrefix(schemaName);
|
|
267
|
+
const columns = await connection.execute(`pragma ${prefix}table_xinfo('${tableName}')`);
|
|
268
|
+
const sql = `select sql from ${prefix}sqlite_master where type = ? and name = ?`;
|
|
158
269
|
const tableDefinition = await connection.execute(sql, ['table', tableName], 'get');
|
|
159
270
|
const composite = columns.reduce((count, col) => count + (col.pk ? 1 : 0), 0) > 1;
|
|
160
271
|
// there can be only one, so naive check like this should be enough
|
|
@@ -185,8 +296,9 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
185
296
|
};
|
|
186
297
|
});
|
|
187
298
|
}
|
|
188
|
-
async getEnumDefinitions(connection, tableName) {
|
|
189
|
-
const
|
|
299
|
+
async getEnumDefinitions(connection, tableName, schemaName) {
|
|
300
|
+
const prefix = this.getSchemaPrefix(schemaName);
|
|
301
|
+
const sql = `select sql from ${prefix}sqlite_master where type = ? and name = ?`;
|
|
190
302
|
const tableDefinition = await connection.execute(sql, ['table', tableName], 'get');
|
|
191
303
|
const checkConstraints = [...(tableDefinition.sql.match(/[`["'][^`\]"']+[`\]"'] text check \(.*?\)/gi) ?? [])];
|
|
192
304
|
return checkConstraints.reduce((o, item) => {
|
|
@@ -201,14 +313,16 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
201
313
|
}, {});
|
|
202
314
|
}
|
|
203
315
|
async getPrimaryKeys(connection, indexes, tableName, schemaName) {
|
|
204
|
-
const
|
|
316
|
+
const prefix = this.getSchemaPrefix(schemaName);
|
|
317
|
+
const sql = `pragma ${prefix}table_info(\`${tableName}\`)`;
|
|
205
318
|
const cols = await connection.execute(sql);
|
|
206
319
|
return cols.filter(col => !!col.pk).map(col => col.name);
|
|
207
320
|
}
|
|
208
321
|
async getIndexes(connection, tableName, schemaName) {
|
|
209
|
-
const
|
|
322
|
+
const prefix = this.getSchemaPrefix(schemaName);
|
|
323
|
+
const sql = `pragma ${prefix}table_info(\`${tableName}\`)`;
|
|
210
324
|
const cols = await connection.execute(sql);
|
|
211
|
-
const indexes = await connection.execute(`pragma index_list(\`${tableName}\`)`);
|
|
325
|
+
const indexes = await connection.execute(`pragma ${prefix}index_list(\`${tableName}\`)`);
|
|
212
326
|
const ret = [];
|
|
213
327
|
for (const col of cols.filter(c => c.pk)) {
|
|
214
328
|
ret.push({
|
|
@@ -220,7 +334,7 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
220
334
|
});
|
|
221
335
|
}
|
|
222
336
|
for (const index of indexes.filter(index => !this.isImplicitIndex(index.name))) {
|
|
223
|
-
const res = await connection.execute(`pragma index_info(\`${index.name}\`)`);
|
|
337
|
+
const res = await connection.execute(`pragma ${prefix}index_info(\`${index.name}\`)`);
|
|
224
338
|
ret.push(...res.map(row => ({
|
|
225
339
|
columnNames: [row.name],
|
|
226
340
|
keyName: index.name,
|
|
@@ -259,14 +373,17 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
259
373
|
return checks;
|
|
260
374
|
}
|
|
261
375
|
async getColumnDefinitions(connection, tableName, schemaName) {
|
|
262
|
-
const
|
|
263
|
-
const
|
|
376
|
+
const prefix = this.getSchemaPrefix(schemaName);
|
|
377
|
+
const columns = await connection.execute(`pragma ${prefix}table_xinfo('${tableName}')`);
|
|
378
|
+
const sql = `select sql from ${prefix}sqlite_master where type = ? and name = ?`;
|
|
264
379
|
const tableDefinition = await connection.execute(sql, ['table', tableName], 'get');
|
|
265
380
|
return this.parseTableDefinition(tableDefinition.sql, columns);
|
|
266
381
|
}
|
|
267
382
|
async getForeignKeys(connection, tableName, schemaName) {
|
|
268
383
|
const { constraints } = await this.getColumnDefinitions(connection, tableName, schemaName);
|
|
269
|
-
const
|
|
384
|
+
const prefix = this.getSchemaPrefix(schemaName);
|
|
385
|
+
const fks = await connection.execute(`pragma ${prefix}foreign_key_list(\`${tableName}\`)`);
|
|
386
|
+
const qualifiedTableName = schemaName ? `${schemaName}.${tableName}` : tableName;
|
|
270
387
|
return fks.reduce((ret, fk) => {
|
|
271
388
|
const constraintName = this.platform.getIndexName(tableName, [fk.from], 'foreign');
|
|
272
389
|
const constraint = constraints?.find(c => c.includes(constraintName));
|
|
@@ -274,7 +391,7 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
274
391
|
constraintName,
|
|
275
392
|
columnName: fk.from,
|
|
276
393
|
columnNames: [fk.from],
|
|
277
|
-
localTableName:
|
|
394
|
+
localTableName: qualifiedTableName,
|
|
278
395
|
referencedTableName: fk.table,
|
|
279
396
|
referencedColumnName: fk.to,
|
|
280
397
|
referencedColumnNames: [fk.to],
|
|
@@ -305,6 +422,15 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
305
422
|
dropIndex(table, index, oldIndexName = index.keyName) {
|
|
306
423
|
return `drop index ${this.quote(oldIndexName)}`;
|
|
307
424
|
}
|
|
425
|
+
/**
|
|
426
|
+
* SQLite does not support schema-qualified table names in REFERENCES clauses.
|
|
427
|
+
* Foreign key references can only point to tables in the same database.
|
|
428
|
+
*/
|
|
429
|
+
getReferencedTableName(referencedTableName, schema) {
|
|
430
|
+
const [schemaName, tableName] = this.splitTableName(referencedTableName);
|
|
431
|
+
// Strip any schema prefix - SQLite REFERENCES clause doesn't support it
|
|
432
|
+
return tableName;
|
|
433
|
+
}
|
|
308
434
|
alterTable(diff, safe) {
|
|
309
435
|
const ret = [];
|
|
310
436
|
const [schemaName, tableName] = this.splitTableName(diff.name);
|
package/dialects/sqlite/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/sql",
|
|
3
|
-
"version": "7.0.0-
|
|
3
|
+
"version": "7.0.0-rc.0",
|
|
4
4
|
"description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"node": ">= 22.17.0"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
|
-
"build": "yarn
|
|
44
|
+
"build": "yarn compile && yarn copy",
|
|
45
45
|
"clean": "yarn run -T rimraf ./dist",
|
|
46
46
|
"compile": "yarn run -T tsc -p tsconfig.build.json",
|
|
47
47
|
"copy": "node ../../scripts/copy.mjs"
|
|
@@ -50,13 +50,12 @@
|
|
|
50
50
|
"access": "public"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"kysely": "0.28.
|
|
54
|
-
"sqlstring": "2.3.3"
|
|
53
|
+
"kysely": "0.28.11"
|
|
55
54
|
},
|
|
56
55
|
"devDependencies": {
|
|
57
|
-
"@mikro-orm/core": "^6.6.
|
|
56
|
+
"@mikro-orm/core": "^6.6.4"
|
|
58
57
|
},
|
|
59
58
|
"peerDependencies": {
|
|
60
|
-
"@mikro-orm/core": "7.0.0-
|
|
59
|
+
"@mikro-orm/core": "7.0.0-rc.0"
|
|
61
60
|
}
|
|
62
61
|
}
|
package/plugin/transformer.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export declare class MikroTransformer extends OperationNodeTransformer {
|
|
|
23
23
|
* Global map of all entities involved in the query.
|
|
24
24
|
* Populated during AST transformation and used for result transformation.
|
|
25
25
|
*/
|
|
26
|
-
protected readonly entityMap: Map<string, EntityMetadata<any
|
|
26
|
+
protected readonly entityMap: Map<string, EntityMetadata<any, import("@mikro-orm/core").EntityCtor<any>>>;
|
|
27
27
|
constructor(em: SqlEntityManager, options?: MikroKyselyPluginOptions);
|
|
28
28
|
reset(): void;
|
|
29
29
|
getOutputEntityMap(): Map<string, EntityMetadata>;
|
package/plugin/transformer.js
CHANGED
|
@@ -706,7 +706,7 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
706
706
|
* Find entity metadata by table name or entity name
|
|
707
707
|
*/
|
|
708
708
|
findEntityMetadata(name) {
|
|
709
|
-
const byEntity = this.metadata.
|
|
709
|
+
const byEntity = this.metadata.getByClassName(name, false);
|
|
710
710
|
if (byEntity) {
|
|
711
711
|
return byEntity;
|
|
712
712
|
}
|
package/query/CriteriaNode.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type EntityKey, type EntityProperty, type MetadataStorage } from '@mikro-orm/core';
|
|
1
|
+
import { type EntityKey, type EntityProperty, type MetadataStorage, type RawQueryFragmentSymbol, type EntityName } from '@mikro-orm/core';
|
|
2
2
|
import type { ICriteriaNode, ICriteriaNodeProcessOptions, IQueryBuilder } from '../typings.js';
|
|
3
3
|
/**
|
|
4
4
|
* Helper for working with deeply nested where/orderBy/having criteria. Uses composite pattern to build tree from the payload.
|
|
@@ -7,21 +7,25 @@ import type { ICriteriaNode, ICriteriaNodeProcessOptions, IQueryBuilder } from '
|
|
|
7
7
|
*/
|
|
8
8
|
export declare class CriteriaNode<T extends object> implements ICriteriaNode<T> {
|
|
9
9
|
protected readonly metadata: MetadataStorage;
|
|
10
|
-
readonly entityName:
|
|
10
|
+
readonly entityName: EntityName<T>;
|
|
11
11
|
readonly parent?: ICriteriaNode<T> | undefined;
|
|
12
|
-
readonly key?: EntityKey<T> | undefined;
|
|
12
|
+
readonly key?: (EntityKey<T> | RawQueryFragmentSymbol) | undefined;
|
|
13
|
+
readonly validate: boolean;
|
|
13
14
|
readonly strict: boolean;
|
|
14
15
|
payload: any;
|
|
15
16
|
prop?: EntityProperty<T>;
|
|
16
17
|
index?: number;
|
|
17
|
-
constructor(metadata: MetadataStorage, entityName:
|
|
18
|
+
constructor(metadata: MetadataStorage, entityName: EntityName<T>, parent?: ICriteriaNode<T> | undefined, key?: (EntityKey<T> | RawQueryFragmentSymbol) | undefined, validate?: boolean, strict?: boolean);
|
|
18
19
|
process(qb: IQueryBuilder<T>, options?: ICriteriaNodeProcessOptions): any;
|
|
19
20
|
unwrap(): any;
|
|
20
21
|
shouldInline(payload: any): boolean;
|
|
21
22
|
willAutoJoin(qb: IQueryBuilder<T>, alias?: string, options?: ICriteriaNodeProcessOptions): boolean;
|
|
22
23
|
shouldRename(payload: any): boolean;
|
|
23
24
|
renameFieldToPK<T>(qb: IQueryBuilder<T>, ownerAlias?: string): string;
|
|
24
|
-
getPath(
|
|
25
|
+
getPath(opts?: {
|
|
26
|
+
addIndex?: boolean;
|
|
27
|
+
parentPath?: string;
|
|
28
|
+
}): string;
|
|
25
29
|
private isPivotJoin;
|
|
26
30
|
getPivotPath(path: string): string;
|
|
27
31
|
aliased(field: string, alias?: string): string;
|
package/query/CriteriaNode.js
CHANGED
|
@@ -9,6 +9,7 @@ export class CriteriaNode {
|
|
|
9
9
|
entityName;
|
|
10
10
|
parent;
|
|
11
11
|
key;
|
|
12
|
+
validate;
|
|
12
13
|
strict;
|
|
13
14
|
payload;
|
|
14
15
|
prop;
|
|
@@ -18,9 +19,10 @@ export class CriteriaNode {
|
|
|
18
19
|
this.entityName = entityName;
|
|
19
20
|
this.parent = parent;
|
|
20
21
|
this.key = key;
|
|
22
|
+
this.validate = validate;
|
|
21
23
|
this.strict = strict;
|
|
22
24
|
const meta = parent && metadata.find(parent.entityName);
|
|
23
|
-
if (meta && key) {
|
|
25
|
+
if (meta && key && !RawQueryFragment.isKnownFragmentSymbol(key)) {
|
|
24
26
|
const pks = Utils.splitPrimaryKeys(key);
|
|
25
27
|
if (pks.length > 1) {
|
|
26
28
|
return;
|
|
@@ -29,8 +31,8 @@ export class CriteriaNode {
|
|
|
29
31
|
this.prop = meta.props.find(prop => prop.name === k || (prop.fieldNames?.length === 1 && prop.fieldNames[0] === k && prop.persist !== false));
|
|
30
32
|
const isProp = this.prop || meta.props.find(prop => (prop.fieldNames || []).includes(k));
|
|
31
33
|
// do not validate if the key is prefixed or type casted (e.g. `k::text`)
|
|
32
|
-
if (validate && !isProp && !k.includes('.') && !k.includes('::') && !Utils.isOperator(k)
|
|
33
|
-
throw new Error(`Trying to query by not existing property ${entityName}.${k}`);
|
|
34
|
+
if (validate && !isProp && !k.includes('.') && !k.includes('::') && !Utils.isOperator(k)) {
|
|
35
|
+
throw new Error(`Trying to query by not existing property ${Utils.className(entityName)}.${k}`);
|
|
34
36
|
}
|
|
35
37
|
}
|
|
36
38
|
}
|
|
@@ -50,11 +52,9 @@ export class CriteriaNode {
|
|
|
50
52
|
shouldRename(payload) {
|
|
51
53
|
const type = this.prop ? this.prop.kind : null;
|
|
52
54
|
const composite = this.prop?.joinColumns ? this.prop.joinColumns.length > 1 : false;
|
|
53
|
-
const
|
|
54
|
-
const scalar = payload === null || Utils.isPrimaryKey(payload) || payload instanceof RegExp || payload instanceof Date ||
|
|
55
|
-
const
|
|
56
|
-
const keys = plainObject ? Object.keys(payload) : [];
|
|
57
|
-
const operator = plainObject && keys.every(k => Utils.isOperator(k, false));
|
|
55
|
+
const rawField = RawQueryFragment.isKnownFragmentSymbol(this.key);
|
|
56
|
+
const scalar = payload === null || Utils.isPrimaryKey(payload) || payload instanceof RegExp || payload instanceof Date || rawField;
|
|
57
|
+
const operator = Utils.isPlainObject(payload) && Utils.getObjectQueryKeys(payload).every(k => Utils.isOperator(k, false));
|
|
58
58
|
if (composite) {
|
|
59
59
|
return true;
|
|
60
60
|
}
|
|
@@ -78,13 +78,13 @@ export class CriteriaNode {
|
|
|
78
78
|
}
|
|
79
79
|
return Utils.getPrimaryKeyHash(this.prop.referencedColumnNames.map(col => `${alias}.${col}`));
|
|
80
80
|
}
|
|
81
|
-
getPath(
|
|
81
|
+
getPath(opts) {
|
|
82
82
|
// use index on parent only if we are processing to-many relation
|
|
83
83
|
const addParentIndex = this.prop && [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(this.prop.kind);
|
|
84
|
-
const parentPath = this.parent?.getPath(addParentIndex) ?? this.entityName;
|
|
85
|
-
const index = addIndex && this.index != null ? `[${this.index}]` : '';
|
|
84
|
+
const parentPath = opts?.parentPath ?? this.parent?.getPath({ addIndex: addParentIndex }) ?? Utils.className(this.entityName);
|
|
85
|
+
const index = opts?.addIndex && this.index != null ? `[${this.index}]` : '';
|
|
86
86
|
// ignore group operators to allow easier mapping (e.g. for orderBy)
|
|
87
|
-
const key = this.key && !['$and', '$or', '$not'].includes(this.key) ? '.' + this.key : '';
|
|
87
|
+
const key = this.key && !RawQueryFragment.isKnownFragmentSymbol(this.key) && !['$and', '$or', '$not'].includes(this.key) ? '.' + this.key : '';
|
|
88
88
|
const ret = parentPath + index + key;
|
|
89
89
|
if (this.isPivotJoin()) {
|
|
90
90
|
// distinguish pivot table join from target entity join
|
|
@@ -96,9 +96,9 @@ export class CriteriaNode {
|
|
|
96
96
|
if (!this.key || !this.prop) {
|
|
97
97
|
return false;
|
|
98
98
|
}
|
|
99
|
-
const
|
|
100
|
-
const scalar = this.payload === null || Utils.isPrimaryKey(this.payload) || this.payload instanceof RegExp || this.payload instanceof Date ||
|
|
101
|
-
const operator = Utils.isObject(this.payload) &&
|
|
99
|
+
const rawField = RawQueryFragment.isKnownFragmentSymbol(this.key);
|
|
100
|
+
const scalar = this.payload === null || Utils.isPrimaryKey(this.payload) || this.payload instanceof RegExp || this.payload instanceof Date || rawField;
|
|
101
|
+
const operator = Utils.isObject(this.payload) && Utils.getObjectQueryKeys(this.payload).every(k => Utils.isOperator(k, false));
|
|
102
102
|
return this.prop.kind === ReferenceKind.MANY_TO_MANY && (scalar || operator);
|
|
103
103
|
}
|
|
104
104
|
getPivotPath(path) {
|
|
@@ -111,6 +111,7 @@ export class CriteriaNode {
|
|
|
111
111
|
return this.strict;
|
|
112
112
|
}
|
|
113
113
|
/** @ignore */
|
|
114
|
+
/* v8 ignore next */
|
|
114
115
|
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
115
116
|
const o = {};
|
|
116
117
|
['entityName', 'key', 'index', 'payload']
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { type Dictionary, type EntityKey, type EntityMetadata, type MetadataStorage } from '@mikro-orm/core';
|
|
1
|
+
import { type Dictionary, type EntityKey, type EntityMetadata, type EntityName, type MetadataStorage, type RawQueryFragmentSymbol } from '@mikro-orm/core';
|
|
2
2
|
import type { ICriteriaNode } from '../typings.js';
|
|
3
3
|
/**
|
|
4
4
|
* @internal
|
|
5
5
|
*/
|
|
6
6
|
export declare class CriteriaNodeFactory {
|
|
7
|
-
static createNode<T extends object>(metadata: MetadataStorage, entityName:
|
|
8
|
-
static createScalarNode<T extends object>(metadata: MetadataStorage, entityName:
|
|
9
|
-
static createArrayNode<T extends object>(metadata: MetadataStorage, entityName:
|
|
10
|
-
static createObjectNode<T extends object>(metadata: MetadataStorage, entityName:
|
|
11
|
-
static createObjectItemNode<T extends object>(metadata: MetadataStorage, entityName:
|
|
7
|
+
static createNode<T extends object>(metadata: MetadataStorage, entityName: EntityName<T>, payload: any, parent?: ICriteriaNode<T>, key?: EntityKey<T> | RawQueryFragmentSymbol, validate?: boolean): ICriteriaNode<T>;
|
|
8
|
+
static createScalarNode<T extends object>(metadata: MetadataStorage, entityName: EntityName<T>, payload: any, parent?: ICriteriaNode<T>, key?: EntityKey<T> | RawQueryFragmentSymbol, validate?: boolean): ICriteriaNode<T>;
|
|
9
|
+
static createArrayNode<T extends object>(metadata: MetadataStorage, entityName: EntityName<T>, payload: any[], parent?: ICriteriaNode<T>, key?: EntityKey<T>, validate?: boolean): ICriteriaNode<T>;
|
|
10
|
+
static createObjectNode<T extends object>(metadata: MetadataStorage, entityName: EntityName<T>, payload: Dictionary, parent?: ICriteriaNode<T>, key?: EntityKey<T>, validate?: boolean): ICriteriaNode<T>;
|
|
11
|
+
static createObjectItemNode<T extends object>(metadata: MetadataStorage, entityName: EntityName<T>, node: ICriteriaNode<T>, payload: Dictionary, key: EntityKey<T> | RawQueryFragmentSymbol, meta?: EntityMetadata<T>, validate?: boolean): ICriteriaNode<T>;
|
|
12
12
|
}
|