@mikro-orm/sql 7.0.0-rc.1 → 7.0.0-rc.3

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.
Files changed (47) hide show
  1. package/AbstractSqlConnection.js +2 -1
  2. package/AbstractSqlDriver.d.ts +18 -12
  3. package/AbstractSqlDriver.js +187 -38
  4. package/AbstractSqlPlatform.d.ts +1 -0
  5. package/AbstractSqlPlatform.js +5 -3
  6. package/PivotCollectionPersister.js +2 -2
  7. package/SqlEntityManager.d.ts +5 -4
  8. package/SqlEntityManager.js +5 -5
  9. package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +2 -0
  10. package/dialects/mssql/MsSqlNativeQueryBuilder.js +8 -4
  11. package/dialects/mysql/BaseMySqlPlatform.js +1 -2
  12. package/dialects/mysql/MySqlSchemaHelper.js +21 -10
  13. package/dialects/postgresql/BasePostgreSqlPlatform.js +38 -30
  14. package/dialects/postgresql/PostgreSqlSchemaHelper.js +63 -47
  15. package/dialects/sqlite/BaseSqliteConnection.d.ts +4 -1
  16. package/dialects/sqlite/BaseSqliteConnection.js +4 -0
  17. package/dialects/sqlite/NodeSqliteDialect.d.ts +21 -0
  18. package/dialects/sqlite/NodeSqliteDialect.js +43 -0
  19. package/dialects/sqlite/SqliteDriver.d.ts +12 -0
  20. package/dialects/sqlite/SqliteDriver.js +14 -0
  21. package/dialects/sqlite/{BaseSqlitePlatform.d.ts → SqlitePlatform.d.ts} +5 -2
  22. package/dialects/sqlite/{BaseSqlitePlatform.js → SqlitePlatform.js} +30 -4
  23. package/dialects/sqlite/SqliteSchemaHelper.d.ts +5 -0
  24. package/dialects/sqlite/SqliteSchemaHelper.js +31 -10
  25. package/dialects/sqlite/index.d.ts +3 -1
  26. package/dialects/sqlite/index.js +3 -1
  27. package/package.json +30 -30
  28. package/plugin/transformer.js +17 -16
  29. package/query/CriteriaNode.js +28 -10
  30. package/query/CriteriaNodeFactory.js +5 -1
  31. package/query/NativeQueryBuilder.d.ts +25 -0
  32. package/query/NativeQueryBuilder.js +61 -1
  33. package/query/ObjectCriteriaNode.js +71 -27
  34. package/query/QueryBuilder.d.ts +177 -48
  35. package/query/QueryBuilder.js +233 -54
  36. package/query/QueryBuilderHelper.d.ts +4 -3
  37. package/query/QueryBuilderHelper.js +47 -17
  38. package/query/ScalarCriteriaNode.js +14 -7
  39. package/query/raw.js +1 -1
  40. package/schema/DatabaseSchema.js +21 -15
  41. package/schema/DatabaseTable.js +114 -54
  42. package/schema/SchemaComparator.js +56 -32
  43. package/schema/SchemaHelper.js +28 -8
  44. package/schema/SqlSchemaGenerator.d.ts +2 -1
  45. package/schema/SqlSchemaGenerator.js +15 -8
  46. package/tsconfig.build.tsbuildinfo +1 -1
  47. package/typings.d.ts +15 -4
@@ -12,7 +12,7 @@ class InsertStatement {
12
12
  }
13
13
  getData() {
14
14
  const data = {};
15
- this.keys.forEach((key, idx) => data[key] = this.data[idx]);
15
+ this.keys.forEach((key, idx) => (data[key] = this.data[idx]));
16
16
  return data;
17
17
  }
18
18
  }
@@ -28,7 +28,7 @@ class DeleteStatement {
28
28
  }
29
29
  getCondition() {
30
30
  const cond = {};
31
- this.keys.forEach((key, idx) => cond[key] = this.cond[idx]);
31
+ this.keys.forEach((key, idx) => (cond[key] = this.cond[idx]));
32
32
  return cond;
33
33
  }
34
34
  }
@@ -4,7 +4,7 @@ import type { NativeQueryBuilder } from './query/NativeQueryBuilder.js';
4
4
  import type { QueryBuilder } from './query/QueryBuilder.js';
5
5
  import type { SqlEntityRepository } from './SqlEntityRepository.js';
6
6
  import type { Kysely } from 'kysely';
7
- import type { InferKyselyDB } from './typings.js';
7
+ import type { InferClassEntityDB, InferKyselyDB } from './typings.js';
8
8
  import { type MikroKyselyPluginOptions } from './plugin/index.js';
9
9
  export interface GetKyselyOptions extends MikroKyselyPluginOptions {
10
10
  type?: ConnectionType;
@@ -20,14 +20,15 @@ export declare class SqlEntityManager<Driver extends AbstractSqlDriver = Abstrac
20
20
  /**
21
21
  * Shortcut for `createQueryBuilder()`
22
22
  */
23
- qb<Entity extends object, RootAlias extends string = never>(entityName: EntityName<Entity>, alias?: RootAlias, type?: ConnectionType, loggerContext?: LoggingOptions): QueryBuilder<Entity, RootAlias, never, never, never, "*">;
23
+ qb<Entity extends object, RootAlias extends string = never>(entityName: EntityName<Entity>, alias?: RootAlias, type?: ConnectionType, loggerContext?: LoggingOptions): QueryBuilder<Entity, RootAlias, never, never, never, "*", {}>;
24
24
  /**
25
25
  * Returns configured Kysely instance.
26
26
  */
27
- getKysely<TDB = undefined, TOptions extends GetKyselyOptions = GetKyselyOptions>(options?: TOptions): Kysely<TDB extends undefined ? InferKyselyDB<EntitiesFromManager<this>, TOptions> : TDB>;
27
+ getKysely<TDB = undefined, TOptions extends GetKyselyOptions = GetKyselyOptions>(options?: TOptions): Kysely<TDB extends undefined ? InferKyselyDB<EntitiesFromManager<this>, TOptions> & InferClassEntityDB<AllEntitiesFromManager<this>, TOptions> : TDB>;
28
28
  execute<T extends QueryResult | EntityData<AnyEntity> | EntityData<AnyEntity>[] = EntityData<AnyEntity>[]>(query: string | NativeQueryBuilder | RawQueryFragment, params?: any[], method?: 'all' | 'get' | 'run', loggerContext?: LoggingOptions): Promise<T>;
29
29
  getRepository<T extends object, U extends EntityRepository<T> = SqlEntityRepository<T>>(entityName: EntityName<T>): GetRepository<T, U>;
30
30
  protected applyDiscriminatorCondition<Entity extends object>(entityName: EntityName<Entity>, where: FilterQuery<Entity>): FilterQuery<Entity>;
31
31
  }
32
- type EntitiesFromManager<TEntityManager extends EntityManager<any>> = NonNullable<TEntityManager['~entities']> extends any[] ? (Extract<NonNullable<TEntityManager['~entities']>[number], EntitySchemaWithMeta>) : never;
32
+ type EntitiesFromManager<TEntityManager extends EntityManager<any>> = NonNullable<TEntityManager['~entities']> extends any[] ? Extract<NonNullable<TEntityManager['~entities']>[number], EntitySchemaWithMeta> : never;
33
+ type AllEntitiesFromManager<TEntityManager extends EntityManager<any>> = NonNullable<TEntityManager['~entities']> extends any[] ? NonNullable<TEntityManager['~entities']>[number] : never;
33
34
  export {};
@@ -22,11 +22,11 @@ export class SqlEntityManager extends EntityManager {
22
22
  */
23
23
  getKysely(options = {}) {
24
24
  let kysely = this.getConnection(options.type).getClient();
25
- if (options.columnNamingStrategy != null
26
- || options.tableNamingStrategy != null
27
- || options.processOnCreateHooks != null
28
- || options.processOnUpdateHooks != null
29
- || options.convertValues != null) {
25
+ if (options.columnNamingStrategy != null ||
26
+ options.tableNamingStrategy != null ||
27
+ options.processOnCreateHooks != null ||
28
+ options.processOnUpdateHooks != null ||
29
+ options.convertValues != null) {
30
30
  kysely = kysely.withPlugin(new MikroKyselyPlugin(this, options));
31
31
  }
32
32
  return kysely;
@@ -11,4 +11,6 @@ export declare class MsSqlNativeQueryBuilder extends NativeQueryBuilder {
11
11
  protected compileSelect(): void;
12
12
  protected addLockClause(): void;
13
13
  protected compileTruncate(): void;
14
+ /** MSSQL has no RECURSIVE keyword — CTEs are implicitly recursive. */
15
+ protected getCteKeyword(_hasRecursive: boolean): string;
14
16
  }
@@ -19,6 +19,7 @@ export class MsSqlNativeQueryBuilder extends NativeQueryBuilder {
19
19
  if (this.options.comment) {
20
20
  this.parts.push(...this.options.comment.map(comment => `/* ${comment} */`));
21
21
  }
22
+ this.compileCtes();
22
23
  if (this.options.onConflict && !Utils.isEmpty(Utils.asArray(this.options.data)[0])) {
23
24
  this.compileUpsert();
24
25
  }
@@ -77,9 +78,7 @@ export class MsSqlNativeQueryBuilder extends NativeQueryBuilder {
77
78
  return { prefix: '', suffix: '' };
78
79
  }
79
80
  const returningFields = this.options.returning;
80
- const selections = returningFields
81
- .map(field => `[t].${this.platform.quoteIdentifier(field)}`)
82
- .join(',');
81
+ const selections = returningFields.map(field => `[t].${this.platform.quoteIdentifier(field)}`).join(',');
83
82
  return {
84
83
  prefix: `select top(0) ${selections} into #out from ${this.getTableName()} as t left join ${this.getTableName()} on 0 = 1;`,
85
84
  suffix: `select ${selections} from #out as t; drop table #out`,
@@ -181,7 +180,8 @@ export class MsSqlNativeQueryBuilder extends NativeQueryBuilder {
181
180
  }
182
181
  }
183
182
  addLockClause() {
184
- if (!this.options.lockMode || ![LockMode.PESSIMISTIC_READ, LockMode.PESSIMISTIC_WRITE].includes(this.options.lockMode)) {
183
+ if (!this.options.lockMode ||
184
+ ![LockMode.PESSIMISTIC_READ, LockMode.PESSIMISTIC_WRITE].includes(this.options.lockMode)) {
185
185
  return;
186
186
  }
187
187
  const map = {
@@ -197,4 +197,8 @@ export class MsSqlNativeQueryBuilder extends NativeQueryBuilder {
197
197
  const sql = `delete from ${tableName}; declare @count int = case @@rowcount when 0 then 1 else 0 end; dbcc checkident ('${tableName.replace(/[[\]]/g, '')}', reseed, @count)`;
198
198
  this.parts.push(sql);
199
199
  }
200
+ /** MSSQL has no RECURSIVE keyword — CTEs are implicitly recursive. */
201
+ getCteKeyword(_hasRecursive) {
202
+ return 'with';
203
+ }
200
204
  }
@@ -44,8 +44,7 @@ export class BaseMySqlPlatform extends AbstractSqlPlatform {
44
44
  return JSON.stringify(value);
45
45
  }
46
46
  getJsonIndexDefinition(index) {
47
- return index.columnNames
48
- .map(column => {
47
+ return index.columnNames.map(column => {
49
48
  if (!column.includes('.')) {
50
49
  return column;
51
50
  }
@@ -89,11 +89,13 @@ export class MySqlSchemaHelper extends SchemaHelper {
89
89
  };
90
90
  // Capture column options (prefix length, sort order)
91
91
  if (index.sub_part != null || index.sort_order === 'D') {
92
- indexDef.columns = [{
92
+ indexDef.columns = [
93
+ {
93
94
  name: index.column_name,
94
95
  ...(index.sub_part != null && { length: index.sub_part }),
95
96
  ...(index.sort_order === 'D' && { sort: 'DESC' }),
96
- }];
97
+ },
98
+ ];
97
99
  }
98
100
  // Capture index type for fulltext and spatial indexes
99
101
  if (index.index_type === 'FULLTEXT') {
@@ -149,7 +151,8 @@ export class MySqlSchemaHelper extends SchemaHelper {
149
151
  */
150
152
  getIndexColumns(index) {
151
153
  if (index.columns?.length) {
152
- return index.columns.map(col => {
154
+ return index.columns
155
+ .map(col => {
153
156
  const quotedName = this.quote(col.name);
154
157
  // MySQL supports collation via expression: (column_name COLLATE collation_name)
155
158
  // When collation is specified, wrap in parentheses as an expression
@@ -173,7 +176,8 @@ export class MySqlSchemaHelper extends SchemaHelper {
173
176
  colDef += ` ${col.sort}`;
174
177
  }
175
178
  return colDef;
176
- }).join(', ');
179
+ })
180
+ .join(', ');
177
181
  }
178
182
  return index.columnNames.map(c => this.quote(c)).join(', ');
179
183
  }
@@ -205,20 +209,24 @@ export class MySqlSchemaHelper extends SchemaHelper {
205
209
  from information_schema.columns where table_schema = database() and table_name in (${tables.map(t => this.platform.quoteValue(t.table_name))})
206
210
  order by ordinal_position`;
207
211
  const allColumns = await connection.execute(sql);
208
- const str = (val) => val != null ? '' + val : val;
212
+ const str = (val) => (val != null ? '' + val : val);
209
213
  const extra = (val) => val.replace(/auto_increment|default_generated|(stored|virtual) generated/i, '').trim() || undefined;
210
214
  const ret = {};
211
215
  for (const col of allColumns) {
212
216
  const mappedType = this.platform.getMappedType(col.column_type);
213
- const defaultValue = str(this.normalizeDefaultValue((mappedType.compareAsType() === 'boolean' && ['0', '1'].includes(col.column_default))
217
+ const defaultValue = str(this.normalizeDefaultValue(mappedType.compareAsType() === 'boolean' && ['0', '1'].includes(col.column_default)
214
218
  ? ['false', 'true'][+col.column_default]
215
219
  : col.column_default, col.length));
216
220
  const key = this.getTableKey(col);
217
- const generated = col.generation_expression ? `(${col.generation_expression.replaceAll(`\\'`, `'`)}) ${col.extra.match(/stored generated/i) ? 'stored' : 'virtual'}` : undefined;
221
+ const generated = col.generation_expression
222
+ ? `(${col.generation_expression.replaceAll(`\\'`, `'`)}) ${col.extra.match(/stored generated/i) ? 'stored' : 'virtual'}`
223
+ : undefined;
218
224
  ret[key] ??= [];
219
225
  ret[key].push({
220
226
  name: col.column_name,
221
- type: this.platform.isNumericColumn(mappedType) ? col.column_type.replace(/ unsigned$/, '').replace(/\(\d+\)$/, '') : col.column_type,
227
+ type: this.platform.isNumericColumn(mappedType)
228
+ ? col.column_type.replace(/ unsigned$/, '').replace(/\(\d+\)$/, '')
229
+ : col.column_type,
222
230
  mappedType,
223
231
  unsigned: col.column_type.endsWith(' unsigned'),
224
232
  length: col.length,
@@ -328,7 +336,10 @@ export class MySqlSchemaHelper extends SchemaHelper {
328
336
  const enums = await connection.execute(sql);
329
337
  return enums.reduce((o, item) => {
330
338
  o[item.table_name] ??= {};
331
- o[item.table_name][item.column_name] = item.column_type.match(/enum\((.*)\)/)[1].split(',').map((item) => item.match(/'(.*)'/)[1]);
339
+ o[item.table_name][item.column_name] = item.column_type
340
+ .match(/enum\((.*)\)/)[1]
341
+ .split(',')
342
+ .map((item) => item.match(/'(.*)'/)[1]);
332
343
  return o;
333
344
  }, {});
334
345
  }
@@ -338,7 +349,7 @@ export class MySqlSchemaHelper extends SchemaHelper {
338
349
  }
339
350
  const sql = `select 1 from information_schema.tables where table_name = 'CHECK_CONSTRAINTS' and table_schema = 'information_schema'`;
340
351
  const res = await connection.execute(sql);
341
- return this._cache.supportsCheckConstraints = res.length > 0;
352
+ return (this._cache.supportsCheckConstraints = res.length > 0);
342
353
  }
343
354
  getChecksSQL(tables) {
344
355
  return `select cc.constraint_schema as table_schema, tc.table_name as table_name, cc.constraint_name as name, cc.check_clause as expression
@@ -113,8 +113,10 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
113
113
  }
114
114
  getMappedType(type) {
115
115
  switch (this.extractSimpleType(type)) {
116
- case 'tsvector': return Type.getType(FullTextType);
117
- default: return super.getMappedType(type);
116
+ case 'tsvector':
117
+ return Type.getType(FullTextType);
118
+ default:
119
+ return super.getMappedType(type);
118
120
  }
119
121
  }
120
122
  getRegExpOperator(val, flags) {
@@ -132,7 +134,7 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
132
134
  return { $re: val.source };
133
135
  }
134
136
  isBigIntProperty(prop) {
135
- return super.isBigIntProperty(prop) || (['bigserial', 'int8'].includes(prop.columnTypes?.[0]));
137
+ return super.isBigIntProperty(prop) || ['bigserial', 'int8'].includes(prop.columnTypes?.[0]);
136
138
  }
137
139
  getArrayDeclarationSQL() {
138
140
  return 'text[]';
@@ -166,7 +168,7 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
166
168
  return ['begin'];
167
169
  }
168
170
  marshallArray(values) {
169
- const quote = (v) => v === '' || v.match(/["{},\\]/) ? JSON.stringify(v) : v;
171
+ const quote = (v) => (v === '' || v.match(/["{},\\]/) ? JSON.stringify(v) : v);
170
172
  return `{${values.map(v => quote('' + v)).join(',')}}`;
171
173
  }
172
174
  /* v8 ignore next */
@@ -174,7 +176,10 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
174
176
  if (value === '{}') {
175
177
  return [];
176
178
  }
177
- return value.substring(1, value.length - 1).split(',').map(v => {
179
+ return value
180
+ .substring(1, value.length - 1)
181
+ .split(',')
182
+ .map(v => {
178
183
  if (v === `""`) {
179
184
  return '';
180
185
  }
@@ -218,7 +223,8 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
218
223
  const cast = (key) => raw(type in types ? `(${key})::${types[type]}` : key);
219
224
  let lastOperator = '->>';
220
225
  // force `->` for operator payloads with array values
221
- if (Utils.isPlainObject(value) && Object.keys(value).every(key => ARRAY_OPERATORS.includes(key) && Array.isArray(value[key]))) {
226
+ if (Utils.isPlainObject(value) &&
227
+ Object.keys(value).every(key => ARRAY_OPERATORS.includes(key) && Array.isArray(value[key]))) {
222
228
  lastOperator = '->';
223
229
  }
224
230
  if (path.length === 0) {
@@ -227,8 +233,7 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
227
233
  return cast(`${root}->${path.map(a => this.quoteValue(a)).join('->')}${lastOperator}'${last}'`);
228
234
  }
229
235
  getJsonIndexDefinition(index) {
230
- return index.columnNames
231
- .map(column => {
236
+ return index.columnNames.map(column => {
232
237
  if (!column.includes('.')) {
233
238
  return column;
234
239
  }
@@ -286,26 +291,26 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
286
291
  getDefaultMappedType(type) {
287
292
  const normalizedType = this.extractSimpleType(type);
288
293
  const map = {
289
- 'int2': 'smallint',
290
- 'smallserial': 'smallint',
291
- 'int': 'integer',
292
- 'int4': 'integer',
293
- 'serial': 'integer',
294
- 'serial4': 'integer',
295
- 'int8': 'bigint',
296
- 'bigserial': 'bigint',
297
- 'serial8': 'bigint',
298
- 'numeric': 'decimal',
299
- 'bool': 'boolean',
300
- 'real': 'float',
301
- 'float4': 'float',
302
- 'float8': 'double',
303
- 'timestamp': 'datetime',
304
- 'timestamptz': 'datetime',
305
- 'bytea': 'blob',
306
- 'jsonb': 'json',
294
+ int2: 'smallint',
295
+ smallserial: 'smallint',
296
+ int: 'integer',
297
+ int4: 'integer',
298
+ serial: 'integer',
299
+ serial4: 'integer',
300
+ int8: 'bigint',
301
+ bigserial: 'bigint',
302
+ serial8: 'bigint',
303
+ numeric: 'decimal',
304
+ bool: 'boolean',
305
+ real: 'float',
306
+ float4: 'float',
307
+ float8: 'double',
308
+ timestamp: 'datetime',
309
+ timestamptz: 'datetime',
310
+ bytea: 'blob',
311
+ jsonb: 'json',
307
312
  'character varying': 'varchar',
308
- 'bpchar': 'character',
313
+ bpchar: 'character',
309
314
  };
310
315
  return super.getDefaultMappedType(map[normalizedType] ?? type);
311
316
  }
@@ -339,9 +344,12 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
339
344
  */
340
345
  castColumn(prop) {
341
346
  switch (prop?.columnTypes?.[0]) {
342
- case this.getUuidTypeDeclarationSQL({}): return '::text';
343
- case this.getBooleanTypeDeclarationSQL(): return '::int';
344
- default: return '';
347
+ case this.getUuidTypeDeclarationSQL({}):
348
+ return '::text';
349
+ case this.getBooleanTypeDeclarationSQL():
350
+ return '::int';
351
+ default:
352
+ return '';
345
353
  }
346
354
  }
347
355
  getDefaultClientUrl() {
@@ -1,4 +1,4 @@
1
- import { DeferMode, EnumType, Type, Utils } from '@mikro-orm/core';
1
+ import { DeferMode, EnumType, Type, Utils, } from '@mikro-orm/core';
2
2
  import { SchemaHelper } from '../../schema/SchemaHelper.js';
3
3
  /** PostGIS system views that should be automatically ignored */
4
4
  const POSTGIS_VIEWS = ['geography_columns', 'geometry_columns'];
@@ -22,24 +22,24 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
22
22
  return `create database ${name}`;
23
23
  }
24
24
  getListTablesSQL() {
25
- return `select table_name, table_schema as schema_name, `
26
- + `(select pg_catalog.obj_description(c.oid) from pg_catalog.pg_class c
27
- where c.oid = (select ('"' || table_schema || '"."' || table_name || '"')::regclass::oid) and c.relname = table_name) as table_comment `
28
- + `from information_schema.tables `
29
- + `where ${this.getIgnoredNamespacesConditionSQL('table_schema')} `
30
- + `and table_name != 'geometry_columns' and table_name != 'spatial_ref_sys' and table_type != 'VIEW' `
31
- + `and table_name not in (select inhrelid::regclass::text from pg_inherits) `
32
- + `order by table_name`;
25
+ return (`select table_name, table_schema as schema_name, ` +
26
+ `(select pg_catalog.obj_description(c.oid) from pg_catalog.pg_class c
27
+ where c.oid = (select ('"' || table_schema || '"."' || table_name || '"')::regclass::oid) and c.relname = table_name) as table_comment ` +
28
+ `from information_schema.tables ` +
29
+ `where ${this.getIgnoredNamespacesConditionSQL('table_schema')} ` +
30
+ `and table_name != 'geometry_columns' and table_name != 'spatial_ref_sys' and table_type != 'VIEW' ` +
31
+ `and table_name not in (select inhrelid::regclass::text from pg_inherits) ` +
32
+ `order by table_name`);
33
33
  }
34
34
  getIgnoredViewsCondition() {
35
35
  return POSTGIS_VIEWS.map(v => `table_name != '${v}'`).join(' and ');
36
36
  }
37
37
  getListViewsSQL() {
38
- return `select table_name as view_name, table_schema as schema_name, view_definition `
39
- + `from information_schema.views `
40
- + `where ${this.getIgnoredNamespacesConditionSQL('table_schema')} `
41
- + `and ${this.getIgnoredViewsCondition()} `
42
- + `order by table_name`;
38
+ return (`select table_name as view_name, table_schema as schema_name, view_definition ` +
39
+ `from information_schema.views ` +
40
+ `where ${this.getIgnoredNamespacesConditionSQL('table_schema')} ` +
41
+ `and ${this.getIgnoredViewsCondition()} ` +
42
+ `order by table_name`);
43
43
  }
44
44
  async loadViews(schema, connection) {
45
45
  const views = await connection.execute(this.getListViewsSQL());
@@ -51,10 +51,10 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
51
51
  }
52
52
  }
53
53
  getListMaterializedViewsSQL() {
54
- return `select matviewname as view_name, schemaname as schema_name, definition as view_definition `
55
- + `from pg_matviews `
56
- + `where ${this.getIgnoredNamespacesConditionSQL('schemaname')} `
57
- + `order by matviewname`;
54
+ return (`select matviewname as view_name, schemaname as schema_name, definition as view_definition ` +
55
+ `from pg_matviews ` +
56
+ `where ${this.getIgnoredNamespacesConditionSQL('schemaname')} ` +
57
+ `order by matviewname`);
58
58
  }
59
59
  async loadMaterializedViews(schema, connection, schemaName) {
60
60
  const views = await connection.execute(this.getListMaterializedViewsSQL());
@@ -78,9 +78,9 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
78
78
  return `refresh materialized view${concurrent} ${this.quote(this.getTableName(name, schema))}`;
79
79
  }
80
80
  async getNamespaces(connection) {
81
- const sql = `select schema_name from information_schema.schemata `
82
- + `where ${this.getIgnoredNamespacesConditionSQL()} `
83
- + `order by schema_name`;
81
+ const sql = `select schema_name from information_schema.schemata ` +
82
+ `where ${this.getIgnoredNamespacesConditionSQL()} ` +
83
+ `order by schema_name`;
84
84
  const res = await connection.execute(sql);
85
85
  return res.map(row => row.schema_name);
86
86
  }
@@ -90,13 +90,11 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
90
90
  'tiger',
91
91
  'topology',
92
92
  /* v8 ignore next */
93
- ...this.platform.getConfig().get('schemaGenerator').ignoreSchema ?? [],
94
- ].map(s => this.platform.quoteValue(s)).join(', ');
95
- const ignoredPrefixes = [
96
- 'pg_',
97
- 'crdb_',
98
- '_timescaledb_',
99
- ].map(p => `"${column}" not like '${p}%'`).join(' and ');
93
+ ...(this.platform.getConfig().get('schemaGenerator').ignoreSchema ?? []),
94
+ ]
95
+ .map(s => this.platform.quoteValue(s))
96
+ .join(', ');
97
+ const ignoredPrefixes = ['pg_', 'crdb_', '_timescaledb_'].map(p => `"${column}" not like '${p}%'`).join(' and ');
100
98
  return `${ignoredPrefixes} and "${column}" not in (${ignored})`;
101
99
  }
102
100
  async loadInformationSchema(schema, connection, tables, schemas) {
@@ -130,9 +128,7 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
130
128
  const key = this.getTableKey(index);
131
129
  // Extract INCLUDE columns from expression first, to filter them from key columns
132
130
  const includeMatch = index.expression?.match(/include\s*\(([^)]+)\)/i);
133
- const includeColumns = includeMatch
134
- ? includeMatch[1].split(',').map((col) => unquote(col.trim()))
135
- : [];
131
+ const includeColumns = includeMatch ? includeMatch[1].split(',').map((col) => unquote(col.trim())) : [];
136
132
  // Filter out INCLUDE columns from the column definitions to get only key columns
137
133
  const keyColumnDefs = index.index_def.filter((col) => !includeColumns.includes(unquote(col)));
138
134
  // Parse sort order and NULLS ordering from the full expression
@@ -268,17 +264,18 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
268
264
  where (${[...tablesBySchemas.entries()].map(([schema, tables]) => `(table_schema = ${this.platform.quoteValue(schema)} and table_name in (${tables.map(t => this.platform.quoteValue(t.table_name)).join(',')}))`).join(' or ')})
269
265
  order by ordinal_position`;
270
266
  const allColumns = await connection.execute(sql);
271
- const str = (val) => val != null ? '' + val : val;
267
+ const str = (val) => (val != null ? '' + val : val);
272
268
  const ret = {};
273
269
  for (const col of allColumns) {
274
270
  const mappedType = connection.getPlatform().getMappedType(col.data_type);
275
- const increments = (col.column_default?.includes('nextval') || col.is_identity === 'YES') && connection.getPlatform().isNumericColumn(mappedType);
271
+ const increments = (col.column_default?.includes('nextval') || col.is_identity === 'YES') &&
272
+ connection.getPlatform().isNumericColumn(mappedType);
276
273
  const key = this.getTableKey(col);
277
274
  ret[key] ??= [];
278
- let type = col.data_type.toLowerCase() === 'array'
279
- ? col.udt_name.replace(/^_(.*)$/, '$1[]')
280
- : col.udt_name;
281
- if (col.data_type === 'USER-DEFINED' && col.udt_schema && col.udt_schema !== this.platform.getDefaultSchemaName()) {
275
+ let type = col.data_type.toLowerCase() === 'array' ? col.udt_name.replace(/^_(.*)$/, '$1[]') : col.udt_name;
276
+ if (col.data_type === 'USER-DEFINED' &&
277
+ col.udt_schema &&
278
+ col.udt_schema !== this.platform.getDefaultSchemaName()) {
282
279
  type = `${col.udt_schema}.${type}`;
283
280
  }
284
281
  if (type === 'bpchar') {
@@ -305,7 +302,13 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
305
302
  default: str(this.normalizeDefaultValue(col.column_default, col.length)),
306
303
  unsigned: increments,
307
304
  autoincrement: increments,
308
- generated: col.is_identity === 'YES' ? (col.identity_generation === 'BY DEFAULT' ? 'by default as identity' : 'identity') : (col.generation_expression ? col.generation_expression + ' stored' : undefined),
305
+ generated: col.is_identity === 'YES'
306
+ ? col.identity_generation === 'BY DEFAULT'
307
+ ? 'by default as identity'
308
+ : 'identity'
309
+ : col.generation_expression
310
+ ? col.generation_expression + ' stored'
311
+ : undefined,
309
312
  comment: col.column_comment,
310
313
  };
311
314
  if (nativeEnums?.[column.type]) {
@@ -321,10 +324,16 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
321
324
  const sql = this.getChecksSQL(tablesBySchemas);
322
325
  const allChecks = await connection.execute(sql);
323
326
  const ret = {};
327
+ const seen = new Set();
324
328
  for (const check of allChecks) {
325
329
  const key = this.getTableKey(check);
330
+ const dedupeKey = `${key}:${check.name}`;
331
+ if (seen.has(dedupeKey)) {
332
+ continue;
333
+ }
334
+ seen.add(dedupeKey);
326
335
  ret[key] ??= [];
327
- const m = check.expression.match(/^check \(\((.*)\)\)$/i);
336
+ const m = check.expression.match(/^check \(\((.*)\)\)$/is);
328
337
  const def = m?.[1].replace(/\((.*?)\)::\w+/g, '$1');
329
338
  ret[key].push({
330
339
  name: check.name,
@@ -359,12 +368,17 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
359
368
  }
360
369
  /* v8 ignore next */
361
370
  switch (value) {
362
- case 'r': return 'RESTRICT';
363
- case 'c': return 'CASCADE';
364
- case 'n': return 'SET NULL';
365
- case 'd': return 'SET DEFAULT';
371
+ case 'r':
372
+ return 'RESTRICT';
373
+ case 'c':
374
+ return 'CASCADE';
375
+ case 'n':
376
+ return 'SET NULL';
377
+ case 'd':
378
+ return 'SET DEFAULT';
366
379
  case 'a':
367
- default: return 'NO ACTION';
380
+ default:
381
+ return 'NO ACTION';
368
382
  }
369
383
  }
370
384
  for (const fk of allFks) {
@@ -645,7 +659,8 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
645
659
  */
646
660
  getIndexColumns(index) {
647
661
  if (index.columns?.length) {
648
- return index.columns.map(col => {
662
+ return index.columns
663
+ .map(col => {
649
664
  let colDef = this.quote(col.name);
650
665
  // PostgreSQL supports collation with double quotes
651
666
  if (col.collation) {
@@ -660,7 +675,8 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
660
675
  colDef += ` nulls ${col.nulls}`;
661
676
  }
662
677
  return colDef;
663
- }).join(', ');
678
+ })
679
+ .join(', ');
664
680
  }
665
681
  return index.columnNames.map(c => this.quote(c)).join(', ');
666
682
  }
@@ -702,7 +718,7 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
702
718
  from pg_constraint pgc
703
719
  join pg_namespace nsp on nsp.oid = pgc.connamespace
704
720
  join pg_class cls on pgc.conrelid = cls.oid
705
- join information_schema.constraint_column_usage ccu on pgc.conname = ccu.constraint_name and nsp.nspname = ccu.constraint_schema
721
+ join information_schema.constraint_column_usage ccu on pgc.conname = ccu.constraint_name and nsp.nspname = ccu.constraint_schema and cls.relname = ccu.table_name
706
722
  where contype = 'c' and (${[...tablesBySchemas.entries()].map(([schema, tables]) => `ccu.table_name in (${tables.map(t => this.platform.quoteValue(t.table_name)).join(',')}) and ccu.table_schema = ${this.platform.quoteValue(schema)}`).join(' or ')})
707
723
  order by pgc.conname`;
708
724
  }
@@ -1,5 +1,8 @@
1
+ import { type Dialect } from 'kysely';
2
+ import type { Dictionary } from '@mikro-orm/core';
1
3
  import { AbstractSqlConnection } from '../../AbstractSqlConnection.js';
2
- export declare abstract class BaseSqliteConnection extends AbstractSqlConnection {
4
+ export declare class BaseSqliteConnection extends AbstractSqlConnection {
5
+ createKyselyDialect(options: Dictionary): Dialect;
3
6
  connect(options?: {
4
7
  skipOnConnect?: boolean;
5
8
  }): Promise<void>;
@@ -1,6 +1,10 @@
1
1
  import { CompiledQuery } from 'kysely';
2
2
  import { AbstractSqlConnection } from '../../AbstractSqlConnection.js';
3
3
  export class BaseSqliteConnection extends AbstractSqlConnection {
4
+ createKyselyDialect(options) {
5
+ throw new Error('No SQLite dialect configured. Pass a Kysely dialect via the `driverOptions` config option, ' +
6
+ 'e.g. `new NodeSqliteDialect(...)` for node:sqlite or a custom dialect for other libraries.');
7
+ }
4
8
  async connect(options) {
5
9
  await super.connect(options);
6
10
  await this.getClient().executeQuery(CompiledQuery.raw('pragma foreign_keys = on'));
@@ -0,0 +1,21 @@
1
+ import { SqliteDialect } from 'kysely';
2
+ /**
3
+ * Kysely dialect for `node:sqlite` (Node.js 22.5+, Deno 2.2+).
4
+ *
5
+ * Bridges `node:sqlite`'s `DatabaseSync` to the `better-sqlite3` interface
6
+ * that Kysely's `SqliteDialect` expects.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { SqliteDriver, NodeSqliteDialect } from '@mikro-orm/sql';
11
+ *
12
+ * const orm = await MikroORM.init({
13
+ * driver: SqliteDriver,
14
+ * dbName: ':memory:',
15
+ * driverOptions: new NodeSqliteDialect(':memory:'),
16
+ * });
17
+ * ```
18
+ */
19
+ export declare class NodeSqliteDialect extends SqliteDialect {
20
+ constructor(dbName: string);
21
+ }
@@ -0,0 +1,43 @@
1
+ import { SqliteDialect } from 'kysely';
2
+ /**
3
+ * Kysely dialect for `node:sqlite` (Node.js 22.5+, Deno 2.2+).
4
+ *
5
+ * Bridges `node:sqlite`'s `DatabaseSync` to the `better-sqlite3` interface
6
+ * that Kysely's `SqliteDialect` expects.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { SqliteDriver, NodeSqliteDialect } from '@mikro-orm/sql';
11
+ *
12
+ * const orm = await MikroORM.init({
13
+ * driver: SqliteDriver,
14
+ * dbName: ':memory:',
15
+ * driverOptions: new NodeSqliteDialect(':memory:'),
16
+ * });
17
+ * ```
18
+ */
19
+ export class NodeSqliteDialect extends SqliteDialect {
20
+ constructor(dbName) {
21
+ const { DatabaseSync } = globalThis.process.getBuiltinModule('node:sqlite');
22
+ super({
23
+ database: () => {
24
+ const db = new DatabaseSync(dbName);
25
+ return {
26
+ prepare(sql) {
27
+ const stmt = db.prepare(sql);
28
+ return {
29
+ reader: /^\s*(select|pragma|explain|with)/i.test(sql) || /\breturning\b/i.test(sql),
30
+ all: (params) => stmt.all(...params),
31
+ run: (params) => stmt.run(...params),
32
+ /* v8 ignore next */
33
+ get: (params) => stmt.get(...params),
34
+ };
35
+ },
36
+ close() {
37
+ db.close();
38
+ },
39
+ };
40
+ },
41
+ });
42
+ }
43
+ }
@@ -0,0 +1,12 @@
1
+ import type { Configuration } from '@mikro-orm/core';
2
+ import { AbstractSqlDriver } from '../../AbstractSqlDriver.js';
3
+ import { BaseSqliteConnection } from './BaseSqliteConnection.js';
4
+ /**
5
+ * Generic SQLite driver that uses `driverOptions` for the Kysely dialect.
6
+ * Use this with any SQLite library by passing a Kysely dialect via `driverOptions`.
7
+ *
8
+ * For the default better-sqlite3 experience, use `@mikro-orm/sqlite` instead.
9
+ */
10
+ export declare class SqliteDriver extends AbstractSqlDriver<BaseSqliteConnection> {
11
+ constructor(config: Configuration);
12
+ }