@mikro-orm/sql 7.1.0-dev.8 → 7.1.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 +1 -1
- package/AbstractSqlConnection.js +27 -6
- package/AbstractSqlDriver.d.ts +15 -1
- package/AbstractSqlDriver.js +143 -26
- package/AbstractSqlPlatform.d.ts +15 -3
- package/AbstractSqlPlatform.js +25 -7
- package/PivotCollectionPersister.d.ts +2 -2
- package/PivotCollectionPersister.js +6 -1
- package/README.md +2 -1
- package/SqlEntityManager.d.ts +48 -5
- package/SqlEntityManager.js +77 -7
- package/SqlMikroORM.d.ts +23 -0
- package/SqlMikroORM.js +23 -0
- package/dialects/mysql/BaseMySqlPlatform.d.ts +3 -5
- package/dialects/mysql/BaseMySqlPlatform.js +6 -10
- package/dialects/mysql/MySqlSchemaHelper.d.ts +16 -3
- package/dialects/mysql/MySqlSchemaHelper.js +197 -49
- package/dialects/oracledb/OracleDialect.d.ts +1 -1
- package/dialects/oracledb/OracleDialect.js +2 -1
- package/dialects/postgresql/BasePostgreSqlEntityManager.d.ts +19 -0
- package/dialects/postgresql/BasePostgreSqlEntityManager.js +24 -0
- package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +11 -5
- package/dialects/postgresql/BasePostgreSqlPlatform.js +75 -17
- package/dialects/postgresql/PostgreSqlSchemaHelper.d.ts +31 -1
- package/dialects/postgresql/PostgreSqlSchemaHelper.js +269 -28
- package/dialects/postgresql/index.d.ts +2 -0
- package/dialects/postgresql/index.js +2 -0
- package/dialects/postgresql/typeOverrides.d.ts +14 -0
- package/dialects/postgresql/typeOverrides.js +12 -0
- package/dialects/sqlite/SqlitePlatform.d.ts +2 -1
- package/dialects/sqlite/SqlitePlatform.js +4 -0
- package/dialects/sqlite/SqliteSchemaHelper.d.ts +4 -1
- package/dialects/sqlite/SqliteSchemaHelper.js +49 -19
- package/index.d.ts +2 -0
- package/index.js +2 -0
- package/package.json +4 -4
- package/plugin/transformer.d.ts +11 -3
- package/plugin/transformer.js +138 -29
- package/query/CriteriaNode.d.ts +1 -1
- package/query/CriteriaNode.js +2 -2
- package/query/ObjectCriteriaNode.js +1 -1
- package/query/QueryBuilder.d.ts +42 -1
- package/query/QueryBuilder.js +78 -7
- package/schema/DatabaseSchema.d.ts +29 -2
- package/schema/DatabaseSchema.js +131 -4
- package/schema/DatabaseTable.d.ts +14 -1
- package/schema/DatabaseTable.js +165 -32
- package/schema/SchemaComparator.d.ts +18 -0
- package/schema/SchemaComparator.js +196 -1
- package/schema/SchemaHelper.d.ts +67 -1
- package/schema/SchemaHelper.js +255 -25
- package/schema/SqlSchemaGenerator.d.ts +2 -2
- package/schema/SqlSchemaGenerator.js +40 -10
- package/schema/partitioning.d.ts +13 -0
- package/schema/partitioning.js +326 -0
- package/typings.d.ts +59 -5
|
@@ -7,12 +7,34 @@ export class MySqlSchemaHelper extends SchemaHelper {
|
|
|
7
7
|
'current_timestamp(?)': ['current_timestamp(?)'],
|
|
8
8
|
'0': ['0', 'false'],
|
|
9
9
|
};
|
|
10
|
+
// Greedy `(.+)` so nested CASE expressions inside the predicate don't trip the match on
|
|
11
|
+
// an inner `then <col> end` — the trailing `$` anchor forces the regex engine to extend
|
|
12
|
+
// the capture to the outermost case-end boundary.
|
|
13
|
+
static PARTIAL_INDEX_RE = /^\s*\(\s*case\s+when\s+(.+)\s+then\s+`([^`]+)`\s+end\s*\)\s*$/is;
|
|
10
14
|
getSchemaBeginning(charset, disableForeignKeys) {
|
|
11
15
|
if (disableForeignKeys) {
|
|
12
16
|
return `set names ${charset};\n${this.disableForeignKeysSQL()}\n\n`;
|
|
13
17
|
}
|
|
14
18
|
return `set names ${charset};\n\n`;
|
|
15
19
|
}
|
|
20
|
+
getSetSchemaSQL(schema) {
|
|
21
|
+
return `use ${this.quote(schema)}`;
|
|
22
|
+
}
|
|
23
|
+
getResetSchemaSQL(defaultSchema) {
|
|
24
|
+
return `use ${this.quote(defaultSchema)}`;
|
|
25
|
+
}
|
|
26
|
+
supportsMigrationSchema() {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
async tableExists(connection, tableName, schemaName, ctx) {
|
|
30
|
+
// MySQL "schema" = database — when none is requested, probe the connection's current DB
|
|
31
|
+
// via `schema()` rather than the base impl's `getDefaultSchemaName()` (which is undefined here).
|
|
32
|
+
const schemaClause = schemaName
|
|
33
|
+
? `table_schema = ${this.platform.quoteValue(schemaName)}`
|
|
34
|
+
: `table_schema = schema()`;
|
|
35
|
+
const rows = await connection.execute(`select 1 from information_schema.tables where ${schemaClause} and table_name = ${this.platform.quoteValue(tableName)}`, [], 'all', ctx);
|
|
36
|
+
return rows.length > 0;
|
|
37
|
+
}
|
|
16
38
|
disableForeignKeysSQL() {
|
|
17
39
|
return 'set foreign_key_checks = 0;';
|
|
18
40
|
}
|
|
@@ -31,7 +53,7 @@ export class MySqlSchemaHelper extends SchemaHelper {
|
|
|
31
53
|
return sql;
|
|
32
54
|
}
|
|
33
55
|
getListTablesSQL() {
|
|
34
|
-
return `select table_name as table_name, nullif(table_schema, schema()) as schema_name, table_comment as table_comment from information_schema.tables where table_type = 'BASE TABLE' and table_schema = schema()`;
|
|
56
|
+
return `select table_name as table_name, nullif(table_schema, schema()) as schema_name, table_comment as table_comment, table_collation as table_collation from information_schema.tables where table_type = 'BASE TABLE' and table_schema = schema()`;
|
|
35
57
|
}
|
|
36
58
|
getListViewsSQL() {
|
|
37
59
|
return `select table_name as view_name, nullif(table_schema, schema()) as schema_name, view_definition from information_schema.views where table_schema = schema()`;
|
|
@@ -68,6 +90,7 @@ export class MySqlSchemaHelper extends SchemaHelper {
|
|
|
68
90
|
for (const t of tables) {
|
|
69
91
|
const key = this.getTableKey(t);
|
|
70
92
|
const table = schema.addTable(t.table_name, t.schema_name, t.table_comment);
|
|
93
|
+
table.collation = t.table_collation ?? undefined;
|
|
71
94
|
const pks = await this.getPrimaryKeys(connection, indexes[key], table.name, table.schema);
|
|
72
95
|
table.init(columns[key], indexes[key], checks[key], pks, fks[key], enums[key]);
|
|
73
96
|
if (triggers[key]) {
|
|
@@ -84,8 +107,11 @@ export class MySqlSchemaHelper extends SchemaHelper {
|
|
|
84
107
|
const ret = {};
|
|
85
108
|
for (const index of allIndexes) {
|
|
86
109
|
const key = this.getTableKey(index);
|
|
110
|
+
const partialMatch = !index.column_name && typeof index.expression === 'string'
|
|
111
|
+
? MySqlSchemaHelper.PARTIAL_INDEX_RE.exec(index.expression)
|
|
112
|
+
: null;
|
|
87
113
|
const indexDef = {
|
|
88
|
-
columnNames: [index.column_name],
|
|
114
|
+
columnNames: [partialMatch ? partialMatch[2] : index.column_name],
|
|
89
115
|
keyName: index.index_name,
|
|
90
116
|
unique: !index.non_unique,
|
|
91
117
|
primary: index.index_name === 'PRIMARY',
|
|
@@ -113,7 +139,10 @@ export class MySqlSchemaHelper extends SchemaHelper {
|
|
|
113
139
|
if (index.is_visible === 'NO') {
|
|
114
140
|
indexDef.invisible = true;
|
|
115
141
|
}
|
|
116
|
-
if (
|
|
142
|
+
if (partialMatch) {
|
|
143
|
+
indexDef.where = partialMatch[1].trim();
|
|
144
|
+
}
|
|
145
|
+
else if (!index.column_name || index.expression?.match(/ where /i)) {
|
|
117
146
|
indexDef.expression = index.expression; // required for the `getCreateIndexSQL()` call
|
|
118
147
|
indexDef.expression = this.getCreateIndexSQL(index.table_name, indexDef, !!index.expression);
|
|
119
148
|
}
|
|
@@ -150,40 +179,43 @@ export class MySqlSchemaHelper extends SchemaHelper {
|
|
|
150
179
|
return this.appendMySqlIndexSuffix(sql, index);
|
|
151
180
|
}
|
|
152
181
|
/**
|
|
153
|
-
* Build the column list for a MySQL index
|
|
154
|
-
*
|
|
182
|
+
* Build the column list for a MySQL index. MySQL requires collation via an expression:
|
|
183
|
+
* `(column COLLATE collation_name)`. Partial indexes (`where`) are emulated via functional
|
|
184
|
+
* indexes — requires MySQL 8.0.13+. MariaDB does not support inline functional indexes
|
|
185
|
+
* and overrides to throw at a higher level.
|
|
155
186
|
*/
|
|
156
187
|
getIndexColumns(index) {
|
|
157
|
-
if (index.
|
|
158
|
-
return index
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
return expr;
|
|
171
|
-
}
|
|
172
|
-
// Standard column definition without collation
|
|
173
|
-
let colDef = quotedName;
|
|
174
|
-
// MySQL supports prefix length
|
|
175
|
-
if (col.length) {
|
|
176
|
-
colDef += `(${col.length})`;
|
|
177
|
-
}
|
|
178
|
-
// MySQL supports sort order
|
|
188
|
+
if (index.where) {
|
|
189
|
+
return this.emulatePartialIndexColumns(index);
|
|
190
|
+
}
|
|
191
|
+
return index.columnNames
|
|
192
|
+
.map(name => {
|
|
193
|
+
const col = index.columns?.find(c => c.name === name);
|
|
194
|
+
const quotedName = this.quote(name);
|
|
195
|
+
// MySQL supports collation via expression: (column_name COLLATE collation_name)
|
|
196
|
+
// When collation is specified, wrap in parentheses as an expression
|
|
197
|
+
if (col?.collation) {
|
|
198
|
+
let expr = col.length ? `${quotedName}(${col.length})` : quotedName;
|
|
199
|
+
expr = `(${expr} collate ${col.collation})`;
|
|
200
|
+
// Sort order comes after the expression
|
|
179
201
|
if (col.sort) {
|
|
180
|
-
|
|
202
|
+
expr += ` ${col.sort}`;
|
|
181
203
|
}
|
|
182
|
-
return
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
204
|
+
return expr;
|
|
205
|
+
}
|
|
206
|
+
// Standard column definition without collation
|
|
207
|
+
let colDef = quotedName;
|
|
208
|
+
// MySQL supports prefix length
|
|
209
|
+
if (col?.length) {
|
|
210
|
+
colDef += `(${col.length})`;
|
|
211
|
+
}
|
|
212
|
+
// MySQL supports sort order
|
|
213
|
+
if (col?.sort) {
|
|
214
|
+
colDef += ` ${col.sort}`;
|
|
215
|
+
}
|
|
216
|
+
return colDef;
|
|
217
|
+
})
|
|
218
|
+
.join(', ');
|
|
187
219
|
}
|
|
188
220
|
/**
|
|
189
221
|
* Append MySQL-specific index suffixes like INVISIBLE.
|
|
@@ -196,22 +228,25 @@ export class MySqlSchemaHelper extends SchemaHelper {
|
|
|
196
228
|
return sql;
|
|
197
229
|
}
|
|
198
230
|
async getAllColumns(connection, tables, ctx) {
|
|
199
|
-
const sql = `select table_name as table_name,
|
|
200
|
-
nullif(table_schema, schema()) as schema_name,
|
|
201
|
-
column_name as column_name,
|
|
202
|
-
column_default as column_default,
|
|
203
|
-
nullif(column_comment, '') as column_comment,
|
|
204
|
-
is_nullable as is_nullable,
|
|
205
|
-
data_type as data_type,
|
|
206
|
-
column_type as column_type,
|
|
207
|
-
column_key as column_key,
|
|
208
|
-
extra as extra,
|
|
209
|
-
generation_expression as generation_expression,
|
|
210
|
-
numeric_precision as numeric_precision,
|
|
211
|
-
numeric_scale as numeric_scale,
|
|
212
|
-
ifnull(datetime_precision, character_maximum_length) length
|
|
213
|
-
|
|
214
|
-
|
|
231
|
+
const sql = `select c.table_name as table_name,
|
|
232
|
+
nullif(c.table_schema, schema()) as schema_name,
|
|
233
|
+
c.column_name as column_name,
|
|
234
|
+
c.column_default as column_default,
|
|
235
|
+
nullif(c.column_comment, '') as column_comment,
|
|
236
|
+
c.is_nullable as is_nullable,
|
|
237
|
+
c.data_type as data_type,
|
|
238
|
+
c.column_type as column_type,
|
|
239
|
+
c.column_key as column_key,
|
|
240
|
+
c.extra as extra,
|
|
241
|
+
c.generation_expression as generation_expression,
|
|
242
|
+
c.numeric_precision as numeric_precision,
|
|
243
|
+
c.numeric_scale as numeric_scale,
|
|
244
|
+
ifnull(c.datetime_precision, c.character_maximum_length) length,
|
|
245
|
+
nullif(c.collation_name, t.table_collation) as collation_name
|
|
246
|
+
from information_schema.columns c
|
|
247
|
+
join information_schema.tables t on t.table_schema = c.table_schema and t.table_name = c.table_name
|
|
248
|
+
where c.table_schema = database() and c.table_name in (${tables.map(t => this.platform.quoteValue(t.table_name))})
|
|
249
|
+
order by c.ordinal_position`;
|
|
215
250
|
const allColumns = await connection.execute(sql, [], 'all', ctx);
|
|
216
251
|
const str = (val) => (val != null ? '' + val : val);
|
|
217
252
|
const extra = (val) => val.replace(/auto_increment|default_generated|(stored|virtual) generated/i, '').trim() || undefined;
|
|
@@ -242,6 +277,7 @@ export class MySqlSchemaHelper extends SchemaHelper {
|
|
|
242
277
|
precision: col.numeric_precision,
|
|
243
278
|
scale: col.numeric_scale,
|
|
244
279
|
comment: col.column_comment,
|
|
280
|
+
collation: col.collation_name ?? undefined,
|
|
245
281
|
extra: extra(col.extra),
|
|
246
282
|
generated,
|
|
247
283
|
});
|
|
@@ -289,6 +325,115 @@ export class MySqlSchemaHelper extends SchemaHelper {
|
|
|
289
325
|
}
|
|
290
326
|
return ret.join(';\n');
|
|
291
327
|
}
|
|
328
|
+
createRoutine(routine) {
|
|
329
|
+
if (routine.expression) {
|
|
330
|
+
return routine.expression;
|
|
331
|
+
}
|
|
332
|
+
const name = this.platform.quoteIdentifier(routine.name);
|
|
333
|
+
// MySQL functions reject direction prefixes (function params are always IN); procedures use them.
|
|
334
|
+
const params = routine.params
|
|
335
|
+
.map(p => {
|
|
336
|
+
const dir = routine.type === 'procedure' ? `${p.direction.toUpperCase()} ` : '';
|
|
337
|
+
return `${dir}${this.platform.quoteIdentifier(p.name)} ${p.type}`;
|
|
338
|
+
})
|
|
339
|
+
.join(', ');
|
|
340
|
+
const determinism = routine.deterministic === true ? ' deterministic' : routine.deterministic === false ? ' not deterministic' : '';
|
|
341
|
+
const dataAccess = this.formatDataAccess(routine.dataAccess);
|
|
342
|
+
const security = routine.security === 'definer'
|
|
343
|
+
? ' sql security definer'
|
|
344
|
+
: routine.security === 'invoker'
|
|
345
|
+
? ' sql security invoker'
|
|
346
|
+
: '';
|
|
347
|
+
const comment = routine.comment ? ` comment ${this.platform.quoteValue(routine.comment)}` : '';
|
|
348
|
+
const body = this.wrapRoutineBody(routine.body ?? '');
|
|
349
|
+
if (routine.type === 'procedure') {
|
|
350
|
+
return `create procedure ${name}(${params})${determinism}${dataAccess}${security}${comment} ${body}`;
|
|
351
|
+
}
|
|
352
|
+
const returnType = routine.returns?.type ?? 'text';
|
|
353
|
+
return `create function ${name}(${params}) returns ${returnType}${determinism}${dataAccess}${security}${comment} ${body}`;
|
|
354
|
+
}
|
|
355
|
+
dropRoutine(routine) {
|
|
356
|
+
const kind = routine.type === 'procedure' ? 'procedure' : 'function';
|
|
357
|
+
return `drop ${kind} if exists ${this.platform.quoteIdentifier(routine.name)}`;
|
|
358
|
+
}
|
|
359
|
+
async getAllRoutines(connection) {
|
|
360
|
+
const sql = `
|
|
361
|
+
select
|
|
362
|
+
r.routine_name as name,
|
|
363
|
+
r.routine_schema as schema_name,
|
|
364
|
+
lower(r.routine_type) as type,
|
|
365
|
+
r.routine_definition as body,
|
|
366
|
+
r.dtd_identifier as return_type,
|
|
367
|
+
r.is_deterministic as is_deterministic,
|
|
368
|
+
r.sql_data_access as sql_data_access,
|
|
369
|
+
r.security_type as security_type,
|
|
370
|
+
r.routine_comment as comment
|
|
371
|
+
from information_schema.routines r
|
|
372
|
+
where r.routine_schema = database()
|
|
373
|
+
and r.routine_type in ('PROCEDURE', 'FUNCTION')
|
|
374
|
+
`;
|
|
375
|
+
const [rows, params] = await Promise.all([
|
|
376
|
+
connection.execute(sql),
|
|
377
|
+
this.getAllRoutineParams(connection),
|
|
378
|
+
]);
|
|
379
|
+
return rows.map(row => ({
|
|
380
|
+
name: row.name,
|
|
381
|
+
type: row.type,
|
|
382
|
+
// MySQL has no schema namespace for routines — undefined matches the metadata side.
|
|
383
|
+
schema: undefined,
|
|
384
|
+
body: this.stripRoutineBody(row.body ?? ''),
|
|
385
|
+
deterministic: row.is_deterministic === 'YES',
|
|
386
|
+
dataAccess: this.parseDataAccess(row.sql_data_access),
|
|
387
|
+
security: row.security_type === 'DEFINER' ? 'definer' : 'invoker',
|
|
388
|
+
comment: row.comment || undefined,
|
|
389
|
+
params: params.get(row.name) ?? [],
|
|
390
|
+
returns: row.type === 'function' && row.return_type ? { type: row.return_type } : undefined,
|
|
391
|
+
}));
|
|
392
|
+
}
|
|
393
|
+
async getAllRoutineParams(connection) {
|
|
394
|
+
const sql = `
|
|
395
|
+
select
|
|
396
|
+
specific_name as routine_name,
|
|
397
|
+
parameter_name as name,
|
|
398
|
+
parameter_mode as direction,
|
|
399
|
+
dtd_identifier as type,
|
|
400
|
+
ordinal_position as position
|
|
401
|
+
from information_schema.parameters
|
|
402
|
+
where specific_schema = database()
|
|
403
|
+
and parameter_name is not null
|
|
404
|
+
order by specific_name, ordinal_position
|
|
405
|
+
`;
|
|
406
|
+
const rows = await connection.execute(sql);
|
|
407
|
+
const out = new Map();
|
|
408
|
+
for (const row of rows) {
|
|
409
|
+
if (!out.has(row.routine_name)) {
|
|
410
|
+
out.set(row.routine_name, []);
|
|
411
|
+
}
|
|
412
|
+
out.get(row.routine_name).push({
|
|
413
|
+
name: row.name,
|
|
414
|
+
type: row.type,
|
|
415
|
+
direction: row.direction.toLowerCase(),
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
return out;
|
|
419
|
+
}
|
|
420
|
+
formatDataAccess(access) {
|
|
421
|
+
switch (access) {
|
|
422
|
+
case 'no-sql':
|
|
423
|
+
return ' no sql';
|
|
424
|
+
case 'reads-sql-data':
|
|
425
|
+
return ' reads sql data';
|
|
426
|
+
case 'modifies-sql-data':
|
|
427
|
+
return ' modifies sql data';
|
|
428
|
+
case 'contains-sql':
|
|
429
|
+
return ' contains sql';
|
|
430
|
+
default:
|
|
431
|
+
return '';
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
parseDataAccess(access) {
|
|
435
|
+
return access.toLowerCase().replace(/\s+/g, '-');
|
|
436
|
+
}
|
|
292
437
|
async getAllTriggers(connection, tables) {
|
|
293
438
|
const names = tables.map(t => this.platform.quoteValue(t.table_name)).join(', ');
|
|
294
439
|
const sql = `select trigger_name as trigger_name, event_object_table as table_name, nullif(event_object_schema, schema()) as schema_name,
|
|
@@ -402,10 +547,13 @@ export class MySqlSchemaHelper extends SchemaHelper {
|
|
|
402
547
|
const col = this.createTableColumn(column, table, changedProperties);
|
|
403
548
|
return [`alter table ${table.getQuotedName()} modify ${col}`];
|
|
404
549
|
}
|
|
550
|
+
// MySQL MODIFY/CHANGE resets omitted column attributes to the table default, so collation must
|
|
551
|
+
// be re-emitted on rename and comment-only paths to preserve a non-default column collation.
|
|
405
552
|
getColumnDeclarationSQL(col) {
|
|
406
553
|
let ret = col.type;
|
|
407
554
|
ret += col.unsigned ? ' unsigned' : '';
|
|
408
555
|
ret += col.autoincrement ? ' auto_increment' : '';
|
|
556
|
+
ret += col.collation ? ` ${this.getCollateSQL(col.collation)}` : '';
|
|
409
557
|
ret += ' ';
|
|
410
558
|
ret += col.nullable ? 'null' : 'not null';
|
|
411
559
|
ret += col.default ? ' default ' + col.default : '';
|
|
@@ -46,7 +46,7 @@ declare class OracleConnection implements DatabaseConnection {
|
|
|
46
46
|
sql: string;
|
|
47
47
|
bindParams: unknown[];
|
|
48
48
|
};
|
|
49
|
-
streamQuery<R>(compiledQuery: CompiledQuery,
|
|
49
|
+
streamQuery<R>(compiledQuery: CompiledQuery, chunkSize?: number): AsyncIterableIterator<QueryResult<R>>;
|
|
50
50
|
get connection(): OraclePoolConnection;
|
|
51
51
|
}
|
|
52
52
|
declare class OracleDriver implements Driver {
|
|
@@ -66,13 +66,14 @@ class OracleConnection {
|
|
|
66
66
|
bindParams: query.parameters,
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
|
-
async *streamQuery(compiledQuery,
|
|
69
|
+
async *streamQuery(compiledQuery, chunkSize) {
|
|
70
70
|
const { sql, bindParams } = this.formatQuery(compiledQuery);
|
|
71
71
|
const result = await this.#connection.execute(sql, bindParams, {
|
|
72
72
|
resultSet: true,
|
|
73
73
|
autoCommit: compiledQuery.autoCommit,
|
|
74
74
|
outFormat: OUT_FORMAT_OBJECT,
|
|
75
75
|
...this.#executeOptions,
|
|
76
|
+
...(chunkSize != null ? { fetchArraySize: chunkSize } : {}),
|
|
76
77
|
});
|
|
77
78
|
const rs = result.resultSet;
|
|
78
79
|
try {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type EntityName } from '@mikro-orm/core';
|
|
2
|
+
import { SqlEntityManager } from '../../SqlEntityManager.js';
|
|
3
|
+
import type { AbstractSqlDriver } from '../../AbstractSqlDriver.js';
|
|
4
|
+
/**
|
|
5
|
+
* Shared base class for PostgreSQL-flavoured entity managers (`pg`, `pglite`).
|
|
6
|
+
* Adds Postgres-only helpers on top of `SqlEntityManager`.
|
|
7
|
+
*/
|
|
8
|
+
export declare class BasePostgreSqlEntityManager<Driver extends AbstractSqlDriver = AbstractSqlDriver> extends SqlEntityManager<Driver> {
|
|
9
|
+
/**
|
|
10
|
+
* Refreshes a materialized view.
|
|
11
|
+
*
|
|
12
|
+
* @param entityName - The entity name or class of the materialized view
|
|
13
|
+
* @param options - Optional settings
|
|
14
|
+
* @param options.concurrently - If true, refreshes the view concurrently (requires a unique index on the view)
|
|
15
|
+
*/
|
|
16
|
+
refreshMaterializedView<Entity extends object>(entityName: EntityName<Entity>, options?: {
|
|
17
|
+
concurrently?: boolean;
|
|
18
|
+
}): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { SqlEntityManager } from '../../SqlEntityManager.js';
|
|
2
|
+
/**
|
|
3
|
+
* Shared base class for PostgreSQL-flavoured entity managers (`pg`, `pglite`).
|
|
4
|
+
* Adds Postgres-only helpers on top of `SqlEntityManager`.
|
|
5
|
+
*/
|
|
6
|
+
export class BasePostgreSqlEntityManager extends SqlEntityManager {
|
|
7
|
+
/**
|
|
8
|
+
* Refreshes a materialized view.
|
|
9
|
+
*
|
|
10
|
+
* @param entityName - The entity name or class of the materialized view
|
|
11
|
+
* @param options - Optional settings
|
|
12
|
+
* @param options.concurrently - If true, refreshes the view concurrently (requires a unique index on the view)
|
|
13
|
+
*/
|
|
14
|
+
async refreshMaterializedView(entityName, options) {
|
|
15
|
+
const meta = this.getMetadata(entityName);
|
|
16
|
+
if (!meta.view || !meta.materialized) {
|
|
17
|
+
throw new Error(`Entity ${meta.className} is not a materialized view`);
|
|
18
|
+
}
|
|
19
|
+
const helper = this.getDriver().getPlatform().getSchemaHelper();
|
|
20
|
+
const schema = meta.schema ?? this.config.get('schema');
|
|
21
|
+
const sql = helper.refreshMaterializedView(meta.tableName, schema, options?.concurrently);
|
|
22
|
+
await this.execute(sql);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -15,6 +15,7 @@ export declare class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
15
15
|
usesEnumCheckConstraints(): boolean;
|
|
16
16
|
getEnumArrayCheckConstraintExpression(column: string, items: string[]): string;
|
|
17
17
|
supportsMaterializedViews(): boolean;
|
|
18
|
+
supportsPartitionedTables(): boolean;
|
|
18
19
|
supportsCustomPrimaryKeyNames(): boolean;
|
|
19
20
|
getCurrentTimestampSQL(length: number): string;
|
|
20
21
|
getDateTimeTypeDeclarationSQL(column: {
|
|
@@ -46,6 +47,7 @@ export declare class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
46
47
|
precision?: number;
|
|
47
48
|
scale?: number;
|
|
48
49
|
autoincrement?: boolean;
|
|
50
|
+
columnTypes?: string[];
|
|
49
51
|
}): string;
|
|
50
52
|
getMappedType(type: string): Type<unknown>;
|
|
51
53
|
getRegExpOperator(val?: unknown, flags?: string): string;
|
|
@@ -69,6 +71,12 @@ export declare class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
69
71
|
}): string[];
|
|
70
72
|
marshallArray(values: string[]): string;
|
|
71
73
|
unmarshallArray(value: string): string[];
|
|
74
|
+
escape(value: any): string;
|
|
75
|
+
/**
|
|
76
|
+
* Ported from PostgreSQL 9.2.4 source code (`src/interfaces/libpq/fe-exec.c`),
|
|
77
|
+
* matching `pg.Client.prototype.escapeLiteral` so we don't need a `pg` dep here.
|
|
78
|
+
*/
|
|
79
|
+
private escapeLiteral;
|
|
72
80
|
getVarcharTypeDeclarationSQL(column: {
|
|
73
81
|
length?: number;
|
|
74
82
|
}): string;
|
|
@@ -92,11 +100,8 @@ export declare class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
92
100
|
getDefaultMappedType(type: string): Type<unknown>;
|
|
93
101
|
supportsSchemas(): boolean;
|
|
94
102
|
getDefaultSchemaName(): string | undefined;
|
|
95
|
-
/**
|
|
96
|
-
|
|
97
|
-
* cannot go past 63 character length for identifiers in MySQL
|
|
98
|
-
*/
|
|
99
|
-
getIndexName(tableName: string, columns: string[], type: 'index' | 'unique' | 'foreign' | 'primary' | 'sequence'): string;
|
|
103
|
+
/** Postgres identifier limit (NAMEDATALEN - 1). */
|
|
104
|
+
getMaxIdentifierLength(): number;
|
|
100
105
|
getDefaultPrimaryName(tableName: string, columns: string[]): string;
|
|
101
106
|
/**
|
|
102
107
|
* @inheritDoc
|
|
@@ -110,4 +115,5 @@ export declare class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
110
115
|
}[]): string;
|
|
111
116
|
getJsonArrayElementPropertySQL(alias: string, property: string, type: string): string;
|
|
112
117
|
getDefaultClientUrl(): string;
|
|
118
|
+
caseInsensitiveCollationNames(): boolean;
|
|
113
119
|
}
|
|
@@ -30,6 +30,9 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
30
30
|
supportsMaterializedViews() {
|
|
31
31
|
return true;
|
|
32
32
|
}
|
|
33
|
+
supportsPartitionedTables() {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
33
36
|
supportsCustomPrimaryKeyNames() {
|
|
34
37
|
return true;
|
|
35
38
|
}
|
|
@@ -90,18 +93,27 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
90
93
|
}
|
|
91
94
|
normalizeColumnType(type, options) {
|
|
92
95
|
const simpleType = this.extractSimpleType(type);
|
|
93
|
-
if (['int', 'int4', 'integer'].includes(simpleType)) {
|
|
96
|
+
if (['int', 'int4', 'integer', 'serial'].includes(simpleType)) {
|
|
94
97
|
return this.getIntegerTypeDeclarationSQL({});
|
|
95
98
|
}
|
|
96
|
-
if (['bigint', 'int8'].includes(simpleType)) {
|
|
99
|
+
if (['bigint', 'int8', 'bigserial'].includes(simpleType)) {
|
|
97
100
|
return this.getBigIntTypeDeclarationSQL({});
|
|
98
101
|
}
|
|
99
|
-
if (['smallint', 'int2'].includes(simpleType)) {
|
|
102
|
+
if (['smallint', 'int2', 'smallserial'].includes(simpleType)) {
|
|
100
103
|
return this.getSmallIntTypeDeclarationSQL({});
|
|
101
104
|
}
|
|
102
105
|
if (['boolean', 'bool'].includes(simpleType)) {
|
|
103
106
|
return this.getBooleanTypeDeclarationSQL();
|
|
104
107
|
}
|
|
108
|
+
if (['double', 'double precision', 'float8'].includes(simpleType)) {
|
|
109
|
+
return this.getDoubleDeclarationSQL();
|
|
110
|
+
}
|
|
111
|
+
if (['real', 'float4'].includes(simpleType)) {
|
|
112
|
+
return this.getFloatDeclarationSQL();
|
|
113
|
+
}
|
|
114
|
+
if (['timestamptz', 'timestamp with time zone'].includes(simpleType)) {
|
|
115
|
+
return this.getDateTimeTypeDeclarationSQL(options);
|
|
116
|
+
}
|
|
105
117
|
if (['varchar', 'character varying'].includes(simpleType)) {
|
|
106
118
|
return this.getVarcharTypeDeclarationSQL(options);
|
|
107
119
|
}
|
|
@@ -114,6 +126,12 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
114
126
|
if (['interval'].includes(simpleType)) {
|
|
115
127
|
return this.getIntervalTypeDeclarationSQL(options);
|
|
116
128
|
}
|
|
129
|
+
// TimeType.getColumnType drops the timezone qualifier, so detect tz aliases from the original column type.
|
|
130
|
+
const originalType = options.columnTypes?.[0]?.toLowerCase() ?? type;
|
|
131
|
+
if (/^timetz\b/.test(originalType) || /^time\s+with\s+time\s+zone\b/.test(originalType)) {
|
|
132
|
+
const length = options.length ?? this.getDefaultDateTimeLength();
|
|
133
|
+
return `timetz(${length})`;
|
|
134
|
+
}
|
|
117
135
|
return super.normalizeColumnType(type, options);
|
|
118
136
|
}
|
|
119
137
|
getMappedType(type) {
|
|
@@ -194,6 +212,50 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
194
212
|
return v;
|
|
195
213
|
});
|
|
196
214
|
}
|
|
215
|
+
escape(value) {
|
|
216
|
+
if (typeof value === 'bigint') {
|
|
217
|
+
value = value.toString();
|
|
218
|
+
}
|
|
219
|
+
if (typeof value === 'string') {
|
|
220
|
+
return this.escapeLiteral(value);
|
|
221
|
+
}
|
|
222
|
+
if (value instanceof Date) {
|
|
223
|
+
return `'${this.formatDate(value)}'`;
|
|
224
|
+
}
|
|
225
|
+
if (ArrayBuffer.isView(value)) {
|
|
226
|
+
return `E'\\\\x${value.toString('hex')}'`;
|
|
227
|
+
}
|
|
228
|
+
if (Array.isArray(value)) {
|
|
229
|
+
return value.map(v => this.escape(v)).join(', ');
|
|
230
|
+
}
|
|
231
|
+
return value;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Ported from PostgreSQL 9.2.4 source code (`src/interfaces/libpq/fe-exec.c`),
|
|
235
|
+
* matching `pg.Client.prototype.escapeLiteral` so we don't need a `pg` dep here.
|
|
236
|
+
*/
|
|
237
|
+
escapeLiteral(str) {
|
|
238
|
+
let hasBackslash = false;
|
|
239
|
+
let escaped = `'`;
|
|
240
|
+
for (let i = 0; i < str.length; i++) {
|
|
241
|
+
const c = str[i];
|
|
242
|
+
if (c === `'`) {
|
|
243
|
+
escaped += c + c;
|
|
244
|
+
}
|
|
245
|
+
else if (c === '\\') {
|
|
246
|
+
escaped += c + c;
|
|
247
|
+
hasBackslash = true;
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
escaped += c;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
escaped += `'`;
|
|
254
|
+
if (hasBackslash) {
|
|
255
|
+
escaped = ` E${escaped}`;
|
|
256
|
+
}
|
|
257
|
+
return escaped;
|
|
258
|
+
}
|
|
197
259
|
getVarcharTypeDeclarationSQL(column) {
|
|
198
260
|
if (column.length === -1) {
|
|
199
261
|
return 'varchar';
|
|
@@ -228,9 +290,9 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
228
290
|
lastOperator = '->';
|
|
229
291
|
}
|
|
230
292
|
if (path.length === 0) {
|
|
231
|
-
return cast(`${root}${lastOperator}
|
|
293
|
+
return cast(`${root}${lastOperator}${this.quoteValue(last)}`);
|
|
232
294
|
}
|
|
233
|
-
return cast(`${root}->${path.map(a => this.quoteValue(a)).join('->')}${lastOperator}
|
|
295
|
+
return cast(`${root}->${path.map(a => this.quoteValue(a)).join('->')}${lastOperator}${this.quoteValue(last)}`);
|
|
234
296
|
}
|
|
235
297
|
getJsonIndexDefinition(index) {
|
|
236
298
|
return index.columnNames.map(column => {
|
|
@@ -250,7 +312,8 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
250
312
|
if (RawQueryFragment.isKnownFragment(id)) {
|
|
251
313
|
return super.quoteIdentifier(id);
|
|
252
314
|
}
|
|
253
|
-
|
|
315
|
+
const escaped = id.toString().replaceAll(quote, quote + quote);
|
|
316
|
+
return `${quote}${escaped.replace('.', `${quote}.${quote}`)}${quote}`;
|
|
254
317
|
}
|
|
255
318
|
pad(number, digits) {
|
|
256
319
|
return String(number).padStart(digits, '0');
|
|
@@ -320,17 +383,9 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
320
383
|
getDefaultSchemaName() {
|
|
321
384
|
return 'public';
|
|
322
385
|
}
|
|
323
|
-
/**
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
*/
|
|
327
|
-
getIndexName(tableName, columns, type) {
|
|
328
|
-
const indexName = super.getIndexName(tableName, columns, type);
|
|
329
|
-
if (indexName.length > 63) {
|
|
330
|
-
const suffix = type === 'primary' ? 'pkey' : type;
|
|
331
|
-
return `${indexName.substring(0, 55 - type.length)}_${Utils.hash(indexName, 5)}_${suffix}`;
|
|
332
|
-
}
|
|
333
|
-
return indexName;
|
|
386
|
+
/** Postgres identifier limit (NAMEDATALEN - 1). */
|
|
387
|
+
getMaxIdentifierLength() {
|
|
388
|
+
return 63;
|
|
334
389
|
}
|
|
335
390
|
getDefaultPrimaryName(tableName, columns) {
|
|
336
391
|
const indexName = `${tableName}_pkey`;
|
|
@@ -362,4 +417,7 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
362
417
|
getDefaultClientUrl() {
|
|
363
418
|
return 'postgresql://postgres@127.0.0.1:5432';
|
|
364
419
|
}
|
|
420
|
+
caseInsensitiveCollationNames() {
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
365
423
|
}
|