@mikro-orm/sql 7.0.0-rc.2 → 7.0.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 +5 -4
- package/AbstractSqlConnection.js +20 -6
- package/AbstractSqlDriver.d.ts +19 -13
- package/AbstractSqlDriver.js +225 -47
- package/AbstractSqlPlatform.d.ts +35 -0
- package/AbstractSqlPlatform.js +51 -5
- package/PivotCollectionPersister.d.ts +2 -11
- package/PivotCollectionPersister.js +59 -59
- package/README.md +5 -4
- package/SqlEntityManager.d.ts +2 -2
- package/SqlEntityManager.js +5 -5
- package/dialects/index.d.ts +1 -0
- package/dialects/index.js +1 -0
- package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +2 -0
- package/dialects/mssql/MsSqlNativeQueryBuilder.js +8 -4
- package/dialects/mysql/BaseMySqlPlatform.d.ts +6 -0
- package/dialects/mysql/BaseMySqlPlatform.js +18 -2
- package/dialects/mysql/MySqlSchemaHelper.d.ts +1 -1
- package/dialects/mysql/MySqlSchemaHelper.js +25 -14
- package/dialects/oracledb/OracleDialect.d.ts +78 -0
- package/dialects/oracledb/OracleDialect.js +166 -0
- package/dialects/oracledb/OracleNativeQueryBuilder.d.ts +19 -0
- package/dialects/oracledb/OracleNativeQueryBuilder.js +249 -0
- package/dialects/oracledb/index.d.ts +2 -0
- package/dialects/oracledb/index.js +2 -0
- package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +6 -0
- package/dialects/postgresql/BasePostgreSqlPlatform.js +49 -37
- package/dialects/postgresql/PostgreSqlSchemaHelper.js +75 -59
- package/dialects/sqlite/BaseSqliteConnection.js +2 -2
- package/dialects/sqlite/NodeSqliteDialect.js +3 -1
- package/dialects/sqlite/SqlitePlatform.d.ts +1 -0
- package/dialects/sqlite/SqlitePlatform.js +7 -1
- package/dialects/sqlite/SqliteSchemaHelper.js +23 -17
- package/index.d.ts +1 -1
- package/index.js +0 -1
- package/package.json +30 -30
- package/plugin/index.d.ts +1 -14
- package/plugin/index.js +13 -13
- package/plugin/transformer.d.ts +6 -22
- package/plugin/transformer.js +91 -82
- package/query/ArrayCriteriaNode.d.ts +1 -1
- package/query/CriteriaNode.js +28 -10
- package/query/CriteriaNodeFactory.js +20 -4
- package/query/NativeQueryBuilder.d.ts +28 -3
- package/query/NativeQueryBuilder.js +65 -3
- package/query/ObjectCriteriaNode.js +75 -31
- package/query/QueryBuilder.d.ts +199 -100
- package/query/QueryBuilder.js +544 -358
- package/query/QueryBuilderHelper.d.ts +18 -14
- package/query/QueryBuilderHelper.js +364 -147
- package/query/ScalarCriteriaNode.js +17 -8
- package/query/enums.d.ts +2 -0
- package/query/enums.js +2 -0
- package/query/raw.js +1 -1
- package/schema/DatabaseSchema.d.ts +7 -5
- package/schema/DatabaseSchema.js +68 -45
- package/schema/DatabaseTable.d.ts +8 -6
- package/schema/DatabaseTable.js +191 -107
- package/schema/SchemaComparator.d.ts +1 -3
- package/schema/SchemaComparator.js +76 -50
- package/schema/SchemaHelper.d.ts +2 -13
- package/schema/SchemaHelper.js +30 -9
- package/schema/SqlSchemaGenerator.d.ts +4 -14
- package/schema/SqlSchemaGenerator.js +26 -12
- package/typings.d.ts +10 -5
- package/tsconfig.build.tsbuildinfo +0 -1
|
@@ -7,6 +7,8 @@ import { FullTextType } from './FullTextType.js';
|
|
|
7
7
|
export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
8
8
|
schemaHelper = new PostgreSqlSchemaHelper(this);
|
|
9
9
|
exceptionConverter = new PostgreSqlExceptionConverter();
|
|
10
|
+
/** Maps JS runtime type names to PostgreSQL cast types for JSON property access. @internal */
|
|
11
|
+
#jsonTypeCasts = { number: 'float8', bigint: 'int8', boolean: 'bool' };
|
|
10
12
|
createNativeQueryBuilder() {
|
|
11
13
|
return new PostgreSqlNativeQueryBuilder(this);
|
|
12
14
|
}
|
|
@@ -113,8 +115,10 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
113
115
|
}
|
|
114
116
|
getMappedType(type) {
|
|
115
117
|
switch (this.extractSimpleType(type)) {
|
|
116
|
-
case 'tsvector':
|
|
117
|
-
|
|
118
|
+
case 'tsvector':
|
|
119
|
+
return Type.getType(FullTextType);
|
|
120
|
+
default:
|
|
121
|
+
return super.getMappedType(type);
|
|
118
122
|
}
|
|
119
123
|
}
|
|
120
124
|
getRegExpOperator(val, flags) {
|
|
@@ -132,7 +136,7 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
132
136
|
return { $re: val.source };
|
|
133
137
|
}
|
|
134
138
|
isBigIntProperty(prop) {
|
|
135
|
-
return super.isBigIntProperty(prop) ||
|
|
139
|
+
return super.isBigIntProperty(prop) || ['bigserial', 'int8'].includes(prop.columnTypes?.[0]);
|
|
136
140
|
}
|
|
137
141
|
getArrayDeclarationSQL() {
|
|
138
142
|
return 'text[]';
|
|
@@ -166,7 +170,7 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
166
170
|
return ['begin'];
|
|
167
171
|
}
|
|
168
172
|
marshallArray(values) {
|
|
169
|
-
const quote = (v) => v === '' ||
|
|
173
|
+
const quote = (v) => (v === '' || /["{},\\]/.exec(v) ? JSON.stringify(v) : v);
|
|
170
174
|
return `{${values.map(v => quote('' + v)).join(',')}}`;
|
|
171
175
|
}
|
|
172
176
|
/* v8 ignore next */
|
|
@@ -174,11 +178,14 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
174
178
|
if (value === '{}') {
|
|
175
179
|
return [];
|
|
176
180
|
}
|
|
177
|
-
return value
|
|
181
|
+
return value
|
|
182
|
+
.substring(1, value.length - 1)
|
|
183
|
+
.split(',')
|
|
184
|
+
.map(v => {
|
|
178
185
|
if (v === `""`) {
|
|
179
186
|
return '';
|
|
180
187
|
}
|
|
181
|
-
if (
|
|
188
|
+
if (/"(.*)"/.exec(v)) {
|
|
182
189
|
return v.substring(1, v.length - 1).replaceAll('\\"', '"');
|
|
183
190
|
}
|
|
184
191
|
return v;
|
|
@@ -210,15 +217,11 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
210
217
|
const last = path.pop();
|
|
211
218
|
const root = this.quoteIdentifier(aliased ? `${ALIAS_REPLACEMENT}.${first}` : first);
|
|
212
219
|
type = typeof type === 'string' ? this.getMappedType(type).runtimeType : String(type);
|
|
213
|
-
const
|
|
214
|
-
number: 'float8',
|
|
215
|
-
bigint: 'int8',
|
|
216
|
-
boolean: 'bool',
|
|
217
|
-
};
|
|
218
|
-
const cast = (key) => raw(type in types ? `(${key})::${types[type]}` : key);
|
|
220
|
+
const cast = (key) => raw(type in this.#jsonTypeCasts ? `(${key})::${this.#jsonTypeCasts[type]}` : key);
|
|
219
221
|
let lastOperator = '->>';
|
|
220
222
|
// force `->` for operator payloads with array values
|
|
221
|
-
if (Utils.isPlainObject(value) &&
|
|
223
|
+
if (Utils.isPlainObject(value) &&
|
|
224
|
+
Object.keys(value).every(key => ARRAY_OPERATORS.includes(key) && Array.isArray(value[key]))) {
|
|
222
225
|
lastOperator = '->';
|
|
223
226
|
}
|
|
224
227
|
if (path.length === 0) {
|
|
@@ -227,8 +230,7 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
227
230
|
return cast(`${root}->${path.map(a => this.quoteValue(a)).join('->')}${lastOperator}'${last}'`);
|
|
228
231
|
}
|
|
229
232
|
getJsonIndexDefinition(index) {
|
|
230
|
-
return index.columnNames
|
|
231
|
-
.map(column => {
|
|
233
|
+
return index.columnNames.map(column => {
|
|
232
234
|
if (!column.includes('.')) {
|
|
233
235
|
return column;
|
|
234
236
|
}
|
|
@@ -286,26 +288,26 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
286
288
|
getDefaultMappedType(type) {
|
|
287
289
|
const normalizedType = this.extractSimpleType(type);
|
|
288
290
|
const map = {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
291
|
+
int2: 'smallint',
|
|
292
|
+
smallserial: 'smallint',
|
|
293
|
+
int: 'integer',
|
|
294
|
+
int4: 'integer',
|
|
295
|
+
serial: 'integer',
|
|
296
|
+
serial4: 'integer',
|
|
297
|
+
int8: 'bigint',
|
|
298
|
+
bigserial: 'bigint',
|
|
299
|
+
serial8: 'bigint',
|
|
300
|
+
numeric: 'decimal',
|
|
301
|
+
bool: 'boolean',
|
|
302
|
+
real: 'float',
|
|
303
|
+
float4: 'float',
|
|
304
|
+
float8: 'double',
|
|
305
|
+
timestamp: 'datetime',
|
|
306
|
+
timestamptz: 'datetime',
|
|
307
|
+
bytea: 'blob',
|
|
308
|
+
jsonb: 'json',
|
|
307
309
|
'character varying': 'varchar',
|
|
308
|
-
|
|
310
|
+
bpchar: 'character',
|
|
309
311
|
};
|
|
310
312
|
return super.getDefaultMappedType(map[normalizedType] ?? type);
|
|
311
313
|
}
|
|
@@ -339,11 +341,21 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
339
341
|
*/
|
|
340
342
|
castColumn(prop) {
|
|
341
343
|
switch (prop?.columnTypes?.[0]) {
|
|
342
|
-
case this.getUuidTypeDeclarationSQL({}):
|
|
343
|
-
|
|
344
|
-
|
|
344
|
+
case this.getUuidTypeDeclarationSQL({}):
|
|
345
|
+
return '::text';
|
|
346
|
+
case this.getBooleanTypeDeclarationSQL():
|
|
347
|
+
return '::int';
|
|
348
|
+
default:
|
|
349
|
+
return '';
|
|
345
350
|
}
|
|
346
351
|
}
|
|
352
|
+
getJsonArrayFromSQL(column, alias, _properties) {
|
|
353
|
+
return `jsonb_array_elements(${column}) as ${this.quoteIdentifier(alias)}`;
|
|
354
|
+
}
|
|
355
|
+
getJsonArrayElementPropertySQL(alias, property, type) {
|
|
356
|
+
const expr = `${this.quoteIdentifier(alias)}->>${this.quoteValue(property)}`;
|
|
357
|
+
return type in this.#jsonTypeCasts ? `(${expr})::${this.#jsonTypeCasts[type]}` : expr;
|
|
358
|
+
}
|
|
347
359
|
getDefaultClientUrl() {
|
|
348
360
|
return 'postgresql://postgres@127.0.0.1:5432';
|
|
349
361
|
}
|
|
@@ -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'];
|
|
@@ -19,27 +19,27 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
|
|
|
19
19
|
return `set names '${charset}';\n\n`;
|
|
20
20
|
}
|
|
21
21
|
getCreateDatabaseSQL(name) {
|
|
22
|
-
return `create database ${name}`;
|
|
22
|
+
return `create database ${this.quote(name)}`;
|
|
23
23
|
}
|
|
24
24
|
getListTablesSQL() {
|
|
25
|
-
return `select table_name, table_schema as schema_name, `
|
|
26
|
-
|
|
27
|
-
where c.oid = (select ('"' || table_schema || '"."' || table_name || '"')::regclass::oid) and c.relname = table_name) as table_comment `
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
83
|
-
|
|
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
|
-
]
|
|
95
|
-
|
|
96
|
-
'
|
|
97
|
-
|
|
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
|
|
@@ -156,7 +152,7 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
|
|
|
156
152
|
if (index.condeferrable) {
|
|
157
153
|
indexDef.deferMode = index.condeferred ? DeferMode.INITIALLY_DEFERRED : DeferMode.INITIALLY_IMMEDIATE;
|
|
158
154
|
}
|
|
159
|
-
if (index.index_def.some((col) =>
|
|
155
|
+
if (index.index_def.some((col) => /[(): ,"'`]/.exec(col)) || index.expression?.match(/ where /i)) {
|
|
160
156
|
indexDef.expression = index.expression;
|
|
161
157
|
}
|
|
162
158
|
if (index.deferrable) {
|
|
@@ -194,7 +190,7 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
|
|
|
194
190
|
// Extract just the column list from the expression (between first parens after USING)
|
|
195
191
|
// Pattern: ... USING method (...columns...) [INCLUDE (...)] [WHERE ...]
|
|
196
192
|
// Note: pg_get_indexdef always returns a valid expression with USING clause
|
|
197
|
-
const usingMatch =
|
|
193
|
+
const usingMatch = /using\s+\w+\s*\(/i.exec(expression);
|
|
198
194
|
const startIdx = usingMatch.index + usingMatch[0].length - 1; // Position of opening (
|
|
199
195
|
const columnsStr = this.extractParenthesizedContent(expression, startIdx);
|
|
200
196
|
// Use the column names from columnDefs and find their modifiers in the expression
|
|
@@ -213,12 +209,12 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
|
|
|
213
209
|
result.sort = 'DESC';
|
|
214
210
|
}
|
|
215
211
|
// Extract NULLS ordering
|
|
216
|
-
const nullsMatch =
|
|
212
|
+
const nullsMatch = /nulls\s+(first|last)/i.exec(modifiers);
|
|
217
213
|
if (nullsMatch) {
|
|
218
214
|
result.nulls = nullsMatch[1].toUpperCase();
|
|
219
215
|
}
|
|
220
216
|
// Extract collation
|
|
221
|
-
const collateMatch =
|
|
217
|
+
const collateMatch = /collate\s+"?([^"\s,)]+)"?/i.exec(modifiers);
|
|
222
218
|
if (collateMatch) {
|
|
223
219
|
result.collation = collateMatch[1];
|
|
224
220
|
}
|
|
@@ -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') &&
|
|
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
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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'
|
|
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 =
|
|
336
|
+
const m = /^check \(\((.*)\)\)$/is.exec(check.expression);
|
|
328
337
|
const def = m?.[1].replace(/\((.*?)\)::\w+/g, '$1');
|
|
329
338
|
ret[key].push({
|
|
330
339
|
name: check.name,
|
|
@@ -353,18 +362,23 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
|
|
|
353
362
|
const allFks = await connection.execute(sql);
|
|
354
363
|
const ret = {};
|
|
355
364
|
function mapReferentialIntegrity(value, def) {
|
|
356
|
-
const match = ['n', 'd'].includes(value) &&
|
|
365
|
+
const match = ['n', 'd'].includes(value) && /ON DELETE (SET (NULL|DEFAULT) \(.*?\))/.exec(def);
|
|
357
366
|
if (match) {
|
|
358
367
|
return match[1];
|
|
359
368
|
}
|
|
360
369
|
/* v8 ignore next */
|
|
361
370
|
switch (value) {
|
|
362
|
-
case 'r':
|
|
363
|
-
|
|
364
|
-
case '
|
|
365
|
-
|
|
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:
|
|
380
|
+
default:
|
|
381
|
+
return 'NO ACTION';
|
|
368
382
|
}
|
|
369
383
|
}
|
|
370
384
|
for (const fk of allFks) {
|
|
@@ -451,10 +465,10 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
|
|
|
451
465
|
let items;
|
|
452
466
|
/* v8 ignore next */
|
|
453
467
|
if (m3) {
|
|
454
|
-
items = m3.map((item) =>
|
|
468
|
+
items = m3.map((item) => /^\(?'(.*)'/.exec(item.trim())?.[1]);
|
|
455
469
|
}
|
|
456
470
|
else {
|
|
457
|
-
items = m2[1].split(',').map((item) =>
|
|
471
|
+
items = m2[1].split(',').map((item) => /^\(?'(.*)'/.exec(item.trim())?.[1]);
|
|
458
472
|
}
|
|
459
473
|
items = items.filter(item => item !== undefined);
|
|
460
474
|
if (items.length > 0) {
|
|
@@ -591,7 +605,7 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
|
|
|
591
605
|
if (!defaultValue || typeof defaultValue !== 'string') {
|
|
592
606
|
return super.normalizeDefaultValue(defaultValue, length, PostgreSqlSchemaHelper.DEFAULT_VALUES);
|
|
593
607
|
}
|
|
594
|
-
const match =
|
|
608
|
+
const match = /^'(.*)'::(.*)$/.exec(defaultValue);
|
|
595
609
|
if (match) {
|
|
596
610
|
if (match[2] === 'integer') {
|
|
597
611
|
return +match[1];
|
|
@@ -603,8 +617,8 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
|
|
|
603
617
|
appendComments(table) {
|
|
604
618
|
const sql = [];
|
|
605
619
|
if (table.comment) {
|
|
606
|
-
const comment = this.platform.quoteValue(table.comment)
|
|
607
|
-
sql.push(`comment on table ${table.getQuotedName()} is ${
|
|
620
|
+
const comment = this.platform.quoteValue(this.processComment(table.comment));
|
|
621
|
+
sql.push(`comment on table ${table.getQuotedName()} is ${comment}`);
|
|
608
622
|
}
|
|
609
623
|
for (const column of table.getColumns()) {
|
|
610
624
|
if (column.comment) {
|
|
@@ -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
|
|
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
|
-
})
|
|
678
|
+
})
|
|
679
|
+
.join(', ');
|
|
664
680
|
}
|
|
665
681
|
return index.columnNames.map(c => this.quote(c)).join(', ');
|
|
666
682
|
}
|
|
@@ -702,12 +718,12 @@ 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
|
}
|
|
709
725
|
inferLengthFromColumnType(type) {
|
|
710
|
-
const match =
|
|
726
|
+
const match = /^(\w+(?:\s+\w+)*)\s*(?:\(\s*(\d+)\s*\)|$)/.exec(type);
|
|
711
727
|
if (!match) {
|
|
712
728
|
return;
|
|
713
729
|
}
|
|
@@ -2,8 +2,8 @@ import { CompiledQuery } from 'kysely';
|
|
|
2
2
|
import { AbstractSqlConnection } from '../../AbstractSqlConnection.js';
|
|
3
3
|
export class BaseSqliteConnection extends AbstractSqlConnection {
|
|
4
4
|
createKyselyDialect(options) {
|
|
5
|
-
throw new Error('No SQLite dialect configured. Pass a Kysely dialect via the `driverOptions` config option, '
|
|
6
|
-
|
|
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
7
|
}
|
|
8
8
|
async connect(options) {
|
|
9
9
|
await super.connect(options);
|
|
@@ -75,5 +75,6 @@ export declare class SqlitePlatform extends AbstractSqlPlatform {
|
|
|
75
75
|
convertVersionValue(value: Date | number, prop: EntityProperty): number | {
|
|
76
76
|
$in: (string | number)[];
|
|
77
77
|
};
|
|
78
|
+
getJsonArrayElementPropertySQL(alias: string, property: string, _type: string): string;
|
|
78
79
|
quoteValue(value: any): string;
|
|
79
80
|
}
|
|
@@ -125,11 +125,17 @@ export class SqlitePlatform extends AbstractSqlPlatform {
|
|
|
125
125
|
convertVersionValue(value, prop) {
|
|
126
126
|
if (prop.runtimeType === 'Date') {
|
|
127
127
|
const ts = +value;
|
|
128
|
-
const str = new Date(ts)
|
|
128
|
+
const str = new Date(ts)
|
|
129
|
+
.toISOString()
|
|
130
|
+
.replace('T', ' ')
|
|
131
|
+
.replace(/\.\d{3}Z$/, '');
|
|
129
132
|
return { $in: [ts, str] };
|
|
130
133
|
}
|
|
131
134
|
return value;
|
|
132
135
|
}
|
|
136
|
+
getJsonArrayElementPropertySQL(alias, property, _type) {
|
|
137
|
+
return `json_extract(${this.quoteIdentifier(alias)}.value, '$.${this.quoteJsonKey(property)}')`;
|
|
138
|
+
}
|
|
133
139
|
quoteValue(value) {
|
|
134
140
|
if (value instanceof Date) {
|
|
135
141
|
return '' + +value;
|
|
@@ -31,8 +31,8 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
31
31
|
return '';
|
|
32
32
|
}
|
|
33
33
|
getListTablesSQL() {
|
|
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' `
|
|
35
|
-
|
|
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' ` +
|
|
35
|
+
`union all select name as table_name from sqlite_temp_master where type = 'table' order by name`);
|
|
36
36
|
}
|
|
37
37
|
async getAllTables(connection, schemas) {
|
|
38
38
|
const databases = await this.getDatabaseList(connection);
|
|
@@ -183,9 +183,11 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
183
183
|
getDropColumnsSQL(tableName, columns, schemaName) {
|
|
184
184
|
/* v8 ignore next */
|
|
185
185
|
const name = this.quote((schemaName && schemaName !== this.platform.getDefaultSchemaName() ? schemaName + '.' : '') + tableName);
|
|
186
|
-
return columns
|
|
186
|
+
return columns
|
|
187
|
+
.map(column => {
|
|
187
188
|
return `alter table ${name} drop column ${this.quote(column.name)}`;
|
|
188
|
-
})
|
|
189
|
+
})
|
|
190
|
+
.join(';\n');
|
|
189
191
|
}
|
|
190
192
|
getCreateIndexSQL(tableName, index) {
|
|
191
193
|
/* v8 ignore next */
|
|
@@ -218,7 +220,7 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
218
220
|
const columns = {};
|
|
219
221
|
const constraints = [];
|
|
220
222
|
// extract all columns definitions
|
|
221
|
-
let columnsDef =
|
|
223
|
+
let columnsDef = new RegExp(`create table [\`"']?.*?[\`"']? \\((.*)\\)`, 'i').exec(sql.replaceAll('\n', ''))?.[1];
|
|
222
224
|
/* v8 ignore next */
|
|
223
225
|
if (columnsDef) {
|
|
224
226
|
if (columnsDef.includes(', constraint ')) {
|
|
@@ -228,7 +230,7 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
228
230
|
for (let i = cols.length - 1; i >= 0; i--) {
|
|
229
231
|
const col = cols[i];
|
|
230
232
|
const re = ` *, *[\`"']?${col.name}[\`"']? (.*)`;
|
|
231
|
-
const columnDef =
|
|
233
|
+
const columnDef = new RegExp(re, 'i').exec(columnsDef);
|
|
232
234
|
if (columnDef) {
|
|
233
235
|
columns[col.name] = { name: col.name, definition: columnDef[1] };
|
|
234
236
|
columnsDef = columnsDef.substring(0, columnDef.index);
|
|
@@ -258,7 +260,7 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
258
260
|
* Extracts the SELECT part from a CREATE VIEW statement.
|
|
259
261
|
*/
|
|
260
262
|
extractViewDefinition(viewDefinition) {
|
|
261
|
-
const match =
|
|
263
|
+
const match = /create\s+view\s+[`"']?\w+[`"']?\s+as\s+(.*)/i.exec(viewDefinition);
|
|
262
264
|
/* v8 ignore next - fallback for non-standard view definitions */
|
|
263
265
|
return match ? match[1] : viewDefinition;
|
|
264
266
|
}
|
|
@@ -305,7 +307,11 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
305
307
|
return null;
|
|
306
308
|
}
|
|
307
309
|
// simple values that are returned as-is from pragma (no wrapping needed)
|
|
308
|
-
if (/^-?\d/.test(value) ||
|
|
310
|
+
if (/^-?\d/.test(value) ||
|
|
311
|
+
/^[xX]'/.test(value) ||
|
|
312
|
+
value.startsWith("'") ||
|
|
313
|
+
value.startsWith('"') ||
|
|
314
|
+
value.startsWith('(')) {
|
|
309
315
|
return value;
|
|
310
316
|
}
|
|
311
317
|
const lower = value.toLowerCase();
|
|
@@ -323,10 +329,10 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
323
329
|
return checkConstraints.reduce((o, item) => {
|
|
324
330
|
// check constraints are defined as (note that last closing paren is missing):
|
|
325
331
|
// `type` text check (`type` in ('local', 'global')
|
|
326
|
-
const match =
|
|
332
|
+
const match = /[`["']([^`\]"']+)[`\]"'] text check \(.* \((.*)\)/i.exec(item);
|
|
327
333
|
/* v8 ignore next */
|
|
328
334
|
if (match) {
|
|
329
|
-
o[match[1]] = match[2].split(',').map((item) =>
|
|
335
|
+
o[match[1]] = match[2].split(',').map((item) => /^\(?'(.*)'/.exec(item.trim())[1]);
|
|
330
336
|
}
|
|
331
337
|
return o;
|
|
332
338
|
}, {});
|
|
@@ -369,7 +375,7 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
369
375
|
const checks = [];
|
|
370
376
|
for (const key of Object.keys(columns)) {
|
|
371
377
|
const column = columns[key];
|
|
372
|
-
const expression =
|
|
378
|
+
const expression = / (check \((.*)\))/i.exec(column.definition);
|
|
373
379
|
if (expression) {
|
|
374
380
|
checks.push({
|
|
375
381
|
name: this.platform.getConfig().getNamingStrategy().indexName(tableName, [column.name], 'check'),
|
|
@@ -380,7 +386,7 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
380
386
|
}
|
|
381
387
|
}
|
|
382
388
|
for (const constraint of constraints) {
|
|
383
|
-
const expression =
|
|
389
|
+
const expression = /constraint *[`"']?(.*?)[`"']? * (check \((.*)\))/i.exec(constraint);
|
|
384
390
|
if (expression) {
|
|
385
391
|
checks.push({
|
|
386
392
|
name: expression[1],
|
|
@@ -453,10 +459,10 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
453
459
|
alterTable(diff, safe) {
|
|
454
460
|
const ret = [];
|
|
455
461
|
const [schemaName, tableName] = this.splitTableName(diff.name);
|
|
456
|
-
if (Utils.hasObjectKeys(diff.removedChecks)
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
462
|
+
if (Utils.hasObjectKeys(diff.removedChecks) ||
|
|
463
|
+
Utils.hasObjectKeys(diff.changedChecks) ||
|
|
464
|
+
Utils.hasObjectKeys(diff.changedForeignKeys) ||
|
|
465
|
+
Utils.hasObjectKeys(diff.changedColumns)) {
|
|
460
466
|
return this.getAlterTempTableSQL(diff);
|
|
461
467
|
}
|
|
462
468
|
for (const index of Object.values(diff.removedIndexes)) {
|
|
@@ -496,7 +502,7 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
496
502
|
return ret;
|
|
497
503
|
}
|
|
498
504
|
getAlterTempTableSQL(changedTable) {
|
|
499
|
-
const tempName = `${
|
|
505
|
+
const tempName = `${changedTable.toTable.name}__temp_alter`;
|
|
500
506
|
const quotedName = this.quote(changedTable.toTable.name);
|
|
501
507
|
const quotedTempName = this.quote(tempName);
|
|
502
508
|
const [first, ...rest] = this.createTable(changedTable.toTable);
|
package/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export * from './query/index.js';
|
|
|
13
13
|
export { raw } from './query/index.js';
|
|
14
14
|
export * from './schema/index.js';
|
|
15
15
|
export * from './dialects/index.js';
|
|
16
|
-
export * from './typings.js';
|
|
16
|
+
export type * from './typings.js';
|
|
17
17
|
export * from './plugin/index.js';
|
|
18
18
|
export { SqlEntityManager as EntityManager } from './SqlEntityManager.js';
|
|
19
19
|
export { SqlEntityRepository as EntityRepository } from './SqlEntityRepository.js';
|
package/index.js
CHANGED
|
@@ -13,7 +13,6 @@ export * from './query/index.js';
|
|
|
13
13
|
export { raw } from './query/index.js';
|
|
14
14
|
export * from './schema/index.js';
|
|
15
15
|
export * from './dialects/index.js';
|
|
16
|
-
export * from './typings.js';
|
|
17
16
|
export * from './plugin/index.js';
|
|
18
17
|
export { SqlEntityManager as EntityManager } from './SqlEntityManager.js';
|
|
19
18
|
export { SqlEntityRepository as EntityRepository } from './SqlEntityRepository.js';
|