@mikro-orm/oracledb 7.0.2-dev.8 → 7.0.2

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.
@@ -1,110 +1,111 @@
1
- import { DateTimeType, EnumType, SchemaHelper, StringType, TextType, Utils, } from '@mikro-orm/sql';
1
+ import { DateTimeType, EnumType, SchemaHelper, StringType, TextType, Utils } from '@mikro-orm/sql';
2
+ /** Schema introspection helper for Oracle Database. */
2
3
  export class OracleSchemaHelper extends SchemaHelper {
3
- static DEFAULT_VALUES = {
4
- true: ['1'],
5
- false: ['0'],
6
- systimestamp: ['current_timestamp'],
7
- sysdate: ['current_timestamp'],
8
- };
9
- getDatabaseExistsSQL(name) {
10
- return `select 1 from all_users where username = ${this.platform.quoteValue(name)}`;
11
- }
12
- async getAllTables(connection, schemas) {
13
- if (!schemas || schemas.length === 0) {
14
- return connection.execute(this.getListTablesSQL());
15
- }
16
- const conditions = schemas.map(s => `at.owner = ${this.platform.quoteValue(s)}`).join(' or ');
17
- const sql = `select at.table_name, at.owner as schema_name, atc.comments as table_comment
4
+ static DEFAULT_VALUES = {
5
+ true: ['1'],
6
+ false: ['0'],
7
+ systimestamp: ['current_timestamp'],
8
+ sysdate: ['current_timestamp'],
9
+ };
10
+ getDatabaseExistsSQL(name) {
11
+ return `select 1 from all_users where username = ${this.platform.quoteValue(name)}`;
12
+ }
13
+ async getAllTables(connection, schemas) {
14
+ if (!schemas || schemas.length === 0) {
15
+ return connection.execute(this.getListTablesSQL());
16
+ }
17
+ const conditions = schemas.map(s => `at.owner = ${this.platform.quoteValue(s)}`).join(' or ');
18
+ const sql = `select at.table_name, at.owner as schema_name, atc.comments as table_comment
18
19
  from all_tables at
19
20
  left join all_tab_comments atc on at.owner = atc.owner and at.table_name = atc.table_name
20
21
  where (${conditions})
21
22
  order by schema_name, at.table_name`;
22
- return connection.execute(sql);
23
- }
24
- getListTablesSQL(schemaName) {
25
- /* v8 ignore next: nullish coalescing chain */
26
- const schema = schemaName ?? this.platform.getDefaultSchemaName() ?? '';
27
- /* v8 ignore next 7: Oracle always has a default schema from dbName */
28
- if (!schema) {
29
- return `select at.table_name, at.owner as schema_name, atc.comments as table_comment
23
+ return connection.execute(sql);
24
+ }
25
+ getListTablesSQL(schemaName) {
26
+ /* v8 ignore next: nullish coalescing chain */
27
+ const schema = schemaName ?? this.platform.getDefaultSchemaName() ?? '';
28
+ /* v8 ignore next 7: Oracle always has a default schema from dbName */
29
+ if (!schema) {
30
+ return `select at.table_name, at.owner as schema_name, atc.comments as table_comment
30
31
  from all_tables at
31
32
  left join all_tab_comments atc on at.owner = atc.owner and at.table_name = atc.table_name
32
33
  where ${this.getIgnoredNamespacesConditionSQL('at.owner')}
33
34
  order by schema_name, at.table_name`;
34
- }
35
- return `select at.table_name, at.owner as schema_name, atc.comments as table_comment
35
+ }
36
+ return `select at.table_name, at.owner as schema_name, atc.comments as table_comment
36
37
  from all_tables at
37
38
  left join all_tab_comments atc on at.owner = atc.owner and at.table_name = atc.table_name
38
39
  where at.owner = ${this.platform.quoteValue(schema)}
39
40
  order by schema_name, at.table_name`;
40
- }
41
- getListViewsSQL() {
42
- /* v8 ignore next: schema fallback */
43
- const schema = this.platform.getDefaultSchemaName() ?? '';
44
- return `select view_name, owner as schema_name, text as view_definition
41
+ }
42
+ getListViewsSQL() {
43
+ /* v8 ignore next: schema fallback */
44
+ const schema = this.platform.getDefaultSchemaName() ?? '';
45
+ return `select view_name, owner as schema_name, text as view_definition
45
46
  from all_views
46
47
  where owner = ${this.platform.quoteValue(schema)}
47
48
  order by view_name`;
48
- }
49
- async loadViews(schema, connection) {
50
- const views = await connection.execute(this.getListViewsSQL());
51
- for (const view of views) {
52
- const definition = view.view_definition?.trim();
53
- /* v8 ignore next 4: empty view definition edge case */
54
- if (definition) {
55
- const schemaName = view.schema_name === this.platform.getDefaultSchemaName() ? undefined : view.schema_name;
56
- schema.addView(view.view_name, schemaName, definition);
57
- }
58
- }
59
- }
60
- async getNamespaces(connection) {
61
- const sql = `select username as schema_name from all_users where ${this.getIgnoredNamespacesConditionSQL()} order by username`;
62
- const res = await connection.execute(sql);
63
- return res.map(row => row.schema_name);
64
- }
65
- getIgnoredNamespacesConditionSQL(column = 'username') {
66
- const ignored = [
67
- 'PDBADMIN',
68
- 'ORDS_METADATA',
69
- 'ORDS_PUBLIC_USER',
70
- /* v8 ignore next */
71
- ...(this.platform.getConfig().get('schemaGenerator').ignoreSchema ?? []),
72
- ]
73
- .map(s => this.platform.quoteValue(s))
74
- .join(', ');
75
- return `${column} not in (${ignored}) and oracle_maintained = 'N'`;
76
- }
77
- getDefaultEmptyString() {
78
- return 'null';
79
- }
80
- normalizeDefaultValue(defaultValue, length, defaultValues = {}, stripQuotes = false) {
81
- if (defaultValue == null) {
82
- return defaultValue;
83
- }
84
- // Trim whitespace that Oracle sometimes adds
85
- defaultValue = defaultValue.trim();
86
- let match = /^\((.*)\)$/.exec(defaultValue);
87
- if (match) {
88
- defaultValue = match[1];
89
- }
90
- match = /^\((.*)\)$/.exec(defaultValue);
91
- if (match) {
92
- defaultValue = match[1];
93
- }
94
- match = /^'(.*)'$/.exec(defaultValue);
95
- if (stripQuotes && match) {
96
- defaultValue = match[1];
97
- }
98
- // Normalize current_timestamp variants (Oracle uses CURRENT_TIMESTAMP, SYSTIMESTAMP, etc.)
99
- const lowerDefault = defaultValue.toLowerCase();
100
- if (lowerDefault === 'current_timestamp' || lowerDefault.startsWith('current_timestamp(')) {
101
- // Keep the precision if present
102
- return defaultValue.toLowerCase();
103
- }
104
- return super.normalizeDefaultValue(defaultValue, length, OracleSchemaHelper.DEFAULT_VALUES);
105
- }
106
- async getAllColumns(connection, tablesBySchemas) {
107
- const sql = `select
49
+ }
50
+ async loadViews(schema, connection) {
51
+ const views = await connection.execute(this.getListViewsSQL());
52
+ for (const view of views) {
53
+ const definition = view.view_definition?.trim();
54
+ /* v8 ignore next 4: empty view definition edge case */
55
+ if (definition) {
56
+ const schemaName = view.schema_name === this.platform.getDefaultSchemaName() ? undefined : view.schema_name;
57
+ schema.addView(view.view_name, schemaName, definition);
58
+ }
59
+ }
60
+ }
61
+ async getNamespaces(connection) {
62
+ const sql = `select username as schema_name from all_users where ${this.getIgnoredNamespacesConditionSQL()} order by username`;
63
+ const res = await connection.execute(sql);
64
+ return res.map(row => row.schema_name);
65
+ }
66
+ getIgnoredNamespacesConditionSQL(column = 'username') {
67
+ const ignored = [
68
+ 'PDBADMIN',
69
+ 'ORDS_METADATA',
70
+ 'ORDS_PUBLIC_USER',
71
+ /* v8 ignore next */
72
+ ...(this.platform.getConfig().get('schemaGenerator').ignoreSchema ?? []),
73
+ ]
74
+ .map(s => this.platform.quoteValue(s))
75
+ .join(', ');
76
+ return `${column} not in (${ignored}) and oracle_maintained = 'N'`;
77
+ }
78
+ getDefaultEmptyString() {
79
+ return 'null';
80
+ }
81
+ normalizeDefaultValue(defaultValue, length, defaultValues = {}, stripQuotes = false) {
82
+ if (defaultValue == null) {
83
+ return defaultValue;
84
+ }
85
+ // Trim whitespace that Oracle sometimes adds
86
+ defaultValue = defaultValue.trim();
87
+ let match = /^\((.*)\)$/.exec(defaultValue);
88
+ if (match) {
89
+ defaultValue = match[1];
90
+ }
91
+ match = /^\((.*)\)$/.exec(defaultValue);
92
+ if (match) {
93
+ defaultValue = match[1];
94
+ }
95
+ match = /^'(.*)'$/.exec(defaultValue);
96
+ if (stripQuotes && match) {
97
+ defaultValue = match[1];
98
+ }
99
+ // Normalize current_timestamp variants (Oracle uses CURRENT_TIMESTAMP, SYSTIMESTAMP, etc.)
100
+ const lowerDefault = defaultValue.toLowerCase();
101
+ if (lowerDefault === 'current_timestamp' || lowerDefault.startsWith('current_timestamp(')) {
102
+ // Keep the precision if present
103
+ return defaultValue.toLowerCase();
104
+ }
105
+ return super.normalizeDefaultValue(defaultValue, length, OracleSchemaHelper.DEFAULT_VALUES);
106
+ }
107
+ async getAllColumns(connection, tablesBySchemas) {
108
+ const sql = `select
108
109
  atc.owner as schema_name,
109
110
  atc.table_name as table_name,
110
111
  atc.column_name as column_name,
@@ -126,65 +127,64 @@ export class OracleSchemaHelper extends SchemaHelper {
126
127
  where atc.hidden_column = 'NO'
127
128
  and (${[...tablesBySchemas.entries()].map(([schema, tables]) => `(atc.table_name in (${tables.map(t => this.platform.quoteValue(t.table_name)).join(', ')}) and atc.owner = ${this.platform.quoteValue(schema)})`).join(' or ')})
128
129
  order by atc.owner, atc.table_name, atc.column_id`;
129
- const allColumns = await connection.execute(sql);
130
- const str = (val) => (val != null ? '' + val : val);
131
- const ret = {};
132
- for (const col of allColumns) {
133
- const mappedType = this.platform.getMappedType(col.data_type);
134
- const defaultValue = str(this.normalizeDefaultValue(col.column_default, col.length, {}));
135
- const increments = col.is_identity === 'YES' && connection.getPlatform().isNumericColumn(mappedType);
136
- const key = this.getTableKey(col);
137
- /* v8 ignore next */
138
- const generated = col.generation_expression
139
- ? `${col.generation_expression}${col.is_persisted ? ' persisted' : ''}`
140
- : undefined;
141
- let type = col.data_type;
142
- // Set length based on column type
143
- if (['varchar', 'varchar2', 'char'].includes(col.data_type)) {
144
- col.length = col.character_maximum_length;
145
- }
146
- else if (col.data_type === 'raw') {
147
- // RAW columns use data_length for their size (e.g., raw(16) for UUIDs)
148
- col.length = col.data_length;
149
- }
150
- if (mappedType instanceof DateTimeType) {
151
- col.length = col.datetime_precision;
152
- }
153
- /* v8 ignore next 2: length formatting branch */
154
- if (col.length != null && !type.endsWith(`(${col.length})`) && !['text', 'date'].includes(type)) {
155
- type += `(${col.length === -1 ? 'max' : col.length})`;
156
- }
157
- // Oracle uses 'number' for numeric types (not 'numeric')
158
- if (type === 'number' && col.numeric_precision != null && col.numeric_scale != null) {
159
- type += `(${col.numeric_precision}, ${col.numeric_scale})`;
160
- }
161
- if (type === 'float' && col.numeric_precision != null) {
162
- type += `(${col.numeric_precision})`;
163
- }
164
- ret[key] ??= [];
165
- ret[key].push({
166
- name: col.column_name,
167
- type: this.platform.isNumericColumn(mappedType)
168
- ? col.data_type.replace(/ unsigned$/, '').replace(/\(\d+\)$/, '')
169
- : type,
170
- mappedType,
171
- unsigned: col.data_type.endsWith(' unsigned'),
172
- length: col.length,
173
- default: increments ? undefined : this.wrap(defaultValue, mappedType),
174
- nullable: col.is_nullable === 'Y',
175
- autoincrement: increments,
176
- precision: col.numeric_precision,
177
- scale: col.numeric_scale,
178
- comment: col.column_comment,
179
- generated,
180
- });
181
- }
182
- return ret;
183
- }
184
- async getAllIndexes(connection, tablesBySchemas) {
185
- // Query indexes and join with constraints to identify which indexes back PRIMARY KEY or UNIQUE constraints
186
- // Also join with all_ind_expressions to get function-based index expressions
187
- const sql = `select ind.table_owner as schema_name, ind.table_name, ind.index_name, aic.column_name,
130
+ const allColumns = await connection.execute(sql);
131
+ const str = val => (val != null ? '' + val : val);
132
+ const ret = {};
133
+ for (const col of allColumns) {
134
+ const mappedType = this.platform.getMappedType(col.data_type);
135
+ const defaultValue = str(this.normalizeDefaultValue(col.column_default, col.length, {}));
136
+ const increments = col.is_identity === 'YES' && connection.getPlatform().isNumericColumn(mappedType);
137
+ const key = this.getTableKey(col);
138
+ /* v8 ignore next */
139
+ const generated = col.generation_expression
140
+ ? `${col.generation_expression}${col.is_persisted ? ' persisted' : ''}`
141
+ : undefined;
142
+ let type = col.data_type;
143
+ // Set length based on column type
144
+ if (['varchar', 'varchar2', 'char'].includes(col.data_type)) {
145
+ col.length = col.character_maximum_length;
146
+ } else if (col.data_type === 'raw') {
147
+ // RAW columns use data_length for their size (e.g., raw(16) for UUIDs)
148
+ col.length = col.data_length;
149
+ }
150
+ if (mappedType instanceof DateTimeType) {
151
+ col.length = col.datetime_precision;
152
+ }
153
+ /* v8 ignore next 2: length formatting branch */
154
+ if (col.length != null && !type.endsWith(`(${col.length})`) && !['text', 'date'].includes(type)) {
155
+ type += `(${col.length === -1 ? 'max' : col.length})`;
156
+ }
157
+ // Oracle uses 'number' for numeric types (not 'numeric')
158
+ if (type === 'number' && col.numeric_precision != null && col.numeric_scale != null) {
159
+ type += `(${col.numeric_precision}, ${col.numeric_scale})`;
160
+ }
161
+ if (type === 'float' && col.numeric_precision != null) {
162
+ type += `(${col.numeric_precision})`;
163
+ }
164
+ ret[key] ??= [];
165
+ ret[key].push({
166
+ name: col.column_name,
167
+ type: this.platform.isNumericColumn(mappedType)
168
+ ? col.data_type.replace(/ unsigned$/, '').replace(/\(\d+\)$/, '')
169
+ : type,
170
+ mappedType,
171
+ unsigned: col.data_type.endsWith(' unsigned'),
172
+ length: col.length,
173
+ default: increments ? undefined : this.wrap(defaultValue, mappedType),
174
+ nullable: col.is_nullable === 'Y',
175
+ autoincrement: increments,
176
+ precision: col.numeric_precision,
177
+ scale: col.numeric_scale,
178
+ comment: col.column_comment,
179
+ generated,
180
+ });
181
+ }
182
+ return ret;
183
+ }
184
+ async getAllIndexes(connection, tablesBySchemas) {
185
+ // Query indexes and join with constraints to identify which indexes back PRIMARY KEY or UNIQUE constraints
186
+ // Also join with all_ind_expressions to get function-based index expressions
187
+ const sql = `select ind.table_owner as schema_name, ind.table_name, ind.index_name, aic.column_name,
188
188
  case ind.uniqueness when 'UNIQUE' then 'YES' else 'NO' end as is_unique,
189
189
  case when con.constraint_type = 'P' then 'YES' else 'NO' end as is_primary_key,
190
190
  con.constraint_type,
@@ -196,62 +196,66 @@ export class OracleSchemaHelper extends SchemaHelper {
196
196
  left join all_ind_expressions aie on ind.owner = aie.index_owner and ind.index_name = aie.index_name and aic.column_position = aie.column_position
197
197
  where (${[...tablesBySchemas.entries()].map(([schema, tables]) => `(ind.table_name in (${tables.map(t => this.platform.quoteValue(t.table_name)).join(', ')}) and ind.table_owner = ${this.platform.quoteValue(schema)})`).join(' or ')})
198
198
  order by ind.table_name, ind.index_name, aic.column_position`;
199
- const allIndexes = await connection.execute(sql);
200
- const ret = {};
201
- for (const index of allIndexes) {
202
- const key = this.getTableKey(index);
203
- // If this index backs a PRIMARY KEY or UNIQUE constraint, mark it appropriately
204
- const isPrimary = index.constraint_type === 'P';
205
- const isUniqueConstraint = index.constraint_type === 'U';
206
- const isConstraintIndex = isPrimary || isUniqueConstraint;
207
- // Skip indexes that back PRIMARY KEY constraints - they're handled as part of the table definition
208
- // and should not be managed as separate indexes
209
- if (isPrimary) {
210
- continue;
211
- }
212
- const indexDef = {
213
- columnNames: [index.column_name],
214
- keyName: index.index_name,
215
- unique: index.is_unique === 'YES',
216
- primary: false, // We skip PK indexes above, so this is always false
217
- constraint: isConstraintIndex || index.is_unique === 'YES',
218
- };
219
- // Handle function-based indexes (expression indexes)
220
- /* v8 ignore start: expression index branches */
221
- if (index.expression) {
222
- indexDef.expression = index.expression;
223
- }
224
- else if (index.column_name?.match(/[(): ,"'`]/)) {
225
- indexDef.expression = this.getCreateIndexSQL(index.table_name, indexDef, true);
226
- }
227
- /* v8 ignore stop */
228
- ret[key] ??= [];
229
- ret[key].push(indexDef);
230
- }
231
- for (const key of Object.keys(ret)) {
232
- ret[key] = await this.mapIndexes(ret[key]);
233
- }
234
- return ret;
235
- }
236
- mapForeignKeys(fks, tableName, schemaName) {
237
- const ret = super.mapForeignKeys(fks, tableName, schemaName);
238
- for (const fk of Utils.values(ret)) {
239
- fk.columnNames = Utils.unique(fk.columnNames);
240
- fk.referencedColumnNames = Utils.unique(fk.referencedColumnNames);
241
- }
242
- return ret;
243
- }
244
- createForeignKey(table, foreignKey, alterTable = true, inline = false) {
245
- // Oracle supports ON DELETE CASCADE and ON DELETE SET NULL, but not ON UPDATE
246
- const supportedDeleteRules = ['cascade', 'set null'];
247
- return super.createForeignKey(table, {
248
- ...foreignKey,
249
- updateRule: undefined,
250
- deleteRule: supportedDeleteRules.includes(foreignKey.deleteRule ?? '') ? foreignKey.deleteRule : undefined,
251
- }, alterTable, inline);
252
- }
253
- async getAllForeignKeys(connection, tablesBySchemas) {
254
- const sql = `select fk_cons.constraint_name, fk_cons.table_name, fk_cons.owner as schema_name, fk_cols.column_name,
199
+ const allIndexes = await connection.execute(sql);
200
+ const ret = {};
201
+ for (const index of allIndexes) {
202
+ const key = this.getTableKey(index);
203
+ // If this index backs a PRIMARY KEY or UNIQUE constraint, mark it appropriately
204
+ const isPrimary = index.constraint_type === 'P';
205
+ const isUniqueConstraint = index.constraint_type === 'U';
206
+ const isConstraintIndex = isPrimary || isUniqueConstraint;
207
+ // Skip indexes that back PRIMARY KEY constraints - they're handled as part of the table definition
208
+ // and should not be managed as separate indexes
209
+ if (isPrimary) {
210
+ continue;
211
+ }
212
+ const indexDef = {
213
+ columnNames: [index.column_name],
214
+ keyName: index.index_name,
215
+ unique: index.is_unique === 'YES',
216
+ primary: false, // We skip PK indexes above, so this is always false
217
+ constraint: isConstraintIndex || index.is_unique === 'YES',
218
+ };
219
+ // Handle function-based indexes (expression indexes)
220
+ /* v8 ignore start: expression index branches */
221
+ if (index.expression) {
222
+ indexDef.expression = index.expression;
223
+ } else if (index.column_name?.match(/[(): ,"'`]/)) {
224
+ indexDef.expression = this.getCreateIndexSQL(index.table_name, indexDef, true);
225
+ }
226
+ /* v8 ignore stop */
227
+ ret[key] ??= [];
228
+ ret[key].push(indexDef);
229
+ }
230
+ for (const key of Object.keys(ret)) {
231
+ ret[key] = await this.mapIndexes(ret[key]);
232
+ }
233
+ return ret;
234
+ }
235
+ mapForeignKeys(fks, tableName, schemaName) {
236
+ const ret = super.mapForeignKeys(fks, tableName, schemaName);
237
+ for (const fk of Utils.values(ret)) {
238
+ fk.columnNames = Utils.unique(fk.columnNames);
239
+ fk.referencedColumnNames = Utils.unique(fk.referencedColumnNames);
240
+ }
241
+ return ret;
242
+ }
243
+ createForeignKey(table, foreignKey, alterTable = true, inline = false) {
244
+ // Oracle supports ON DELETE CASCADE and ON DELETE SET NULL, but not ON UPDATE
245
+ const supportedDeleteRules = ['cascade', 'set null'];
246
+ return super.createForeignKey(
247
+ table,
248
+ {
249
+ ...foreignKey,
250
+ updateRule: undefined,
251
+ deleteRule: supportedDeleteRules.includes(foreignKey.deleteRule ?? '') ? foreignKey.deleteRule : undefined,
252
+ },
253
+ alterTable,
254
+ inline,
255
+ );
256
+ }
257
+ async getAllForeignKeys(connection, tablesBySchemas) {
258
+ const sql = `select fk_cons.constraint_name, fk_cons.table_name, fk_cons.owner as schema_name, fk_cols.column_name,
255
259
  fk_cons.r_owner as referenced_schema_name,
256
260
  pk_cols.column_name as referenced_column_name,
257
261
  pk_cons.table_name as referenced_table_name,
@@ -264,46 +268,46 @@ export class OracleSchemaHelper extends SchemaHelper {
264
268
  where fk_cons.constraint_type = 'R'
265
269
  and (${[...tablesBySchemas.entries()].map(([schema, tables]) => `(fk_cons.table_name in (${tables.map(t => this.platform.quoteValue(t.table_name)).join(', ')}) and fk_cons.owner = ${this.platform.quoteValue(schema)})`).join(' or ')})
266
270
  order by fk_cons.owner, fk_cons.table_name, fk_cons.constraint_name, pk_cols.position`;
267
- const allFks = await connection.execute(sql);
268
- const ret = {};
269
- for (const fk of allFks) {
270
- // Oracle returns schema names in uppercase - normalize to lowercase for consistency
271
- fk.schema_name = fk.schema_name?.toLowerCase();
272
- fk.referenced_schema_name = fk.referenced_schema_name?.toLowerCase();
273
- const key = this.getTableKey(fk);
274
- ret[key] ??= [];
275
- ret[key].push(fk);
276
- }
277
- Object.keys(ret).forEach(key => {
278
- const [schemaName, tableName] = key.split('.');
279
- ret[key] = this.mapForeignKeys(ret[key], tableName, schemaName);
280
- });
281
- return ret;
282
- }
283
- getEnumDefinitions(checks) {
284
- return checks.reduce((o, item, index) => {
285
- // check constraints are defined as
286
- // `([type]='owner' OR [type]='manager' OR [type]='employee')`
287
- const m1 = item.definition?.match(/^check \((.*)\)/);
288
- let items = m1?.[1].split(' OR ');
289
- /* v8 ignore next */
290
- const hasItems = (items?.length ?? 0) > 0;
291
- /* v8 ignore next: enum parsing branch */
292
- if (item.columnName && hasItems) {
293
- items = items
294
- .map(val => /^\(?'(.*)'/.exec(val.trim().replace(`"${item.columnName}"=`, ''))?.[1])
295
- .filter(Boolean);
296
- if (items.length > 0) {
297
- o[item.columnName] = items;
298
- }
299
- }
300
- return o;
301
- }, {});
302
- }
303
- getChecksSQL(tablesBySchemas) {
304
- // Filter out NOT NULL constraints using search_condition_vc (Oracle 12c+)
305
- // NOT NULL constraints have expressions like '"column_name" IS NOT NULL'
306
- return `select con.constraint_name as name,
271
+ const allFks = await connection.execute(sql);
272
+ const ret = {};
273
+ for (const fk of allFks) {
274
+ // Oracle returns schema names in uppercase - normalize to lowercase for consistency
275
+ fk.schema_name = fk.schema_name?.toLowerCase();
276
+ fk.referenced_schema_name = fk.referenced_schema_name?.toLowerCase();
277
+ const key = this.getTableKey(fk);
278
+ ret[key] ??= [];
279
+ ret[key].push(fk);
280
+ }
281
+ Object.keys(ret).forEach(key => {
282
+ const [schemaName, tableName] = key.split('.');
283
+ ret[key] = this.mapForeignKeys(ret[key], tableName, schemaName);
284
+ });
285
+ return ret;
286
+ }
287
+ getEnumDefinitions(checks) {
288
+ return checks.reduce((o, item, index) => {
289
+ // check constraints are defined as
290
+ // `([type]='owner' OR [type]='manager' OR [type]='employee')`
291
+ const m1 = item.definition?.match(/^check \((.*)\)/);
292
+ let items = m1?.[1].split(' OR ');
293
+ /* v8 ignore next */
294
+ const hasItems = (items?.length ?? 0) > 0;
295
+ /* v8 ignore next: enum parsing branch */
296
+ if (item.columnName && hasItems) {
297
+ items = items
298
+ .map(val => /^\(?'(.*)'/.exec(val.trim().replace(`"${item.columnName}"=`, ''))?.[1])
299
+ .filter(Boolean);
300
+ if (items.length > 0) {
301
+ o[item.columnName] = items;
302
+ }
303
+ }
304
+ return o;
305
+ }, {});
306
+ }
307
+ getChecksSQL(tablesBySchemas) {
308
+ // Filter out NOT NULL constraints using search_condition_vc (Oracle 12c+)
309
+ // NOT NULL constraints have expressions like '"column_name" IS NOT NULL'
310
+ return `select con.constraint_name as name,
307
311
  con.owner schema_name,
308
312
  con.table_name table_name,
309
313
  (select case when count(acc.column_name) = 1 then min(acc.column_name) else null end
@@ -319,273 +323,279 @@ export class OracleSchemaHelper extends SchemaHelper {
319
323
  and con.search_condition_vc not like '%IS NOT NULL'
320
324
  and (${[...tablesBySchemas.entries()].map(([schema, tables]) => `(con.table_name in (${tables.map(t => this.platform.quoteValue(t.table_name)).join(', ')}) and con.owner = ${this.platform.quoteValue(schema)})`).join(' or ')})
321
325
  order by con.constraint_name`;
322
- }
323
- async getAllChecks(connection, tablesBySchemas) {
324
- const sql = this.getChecksSQL(tablesBySchemas);
325
- const allChecks = await connection.execute(sql);
326
- const ret = {};
327
- for (const check of allChecks) {
328
- const key = this.getTableKey(check);
329
- ret[key] ??= [];
330
- const expression = check.expression.replace(/^\((.*)\)$/, '$1');
331
- ret[key].push({
332
- name: check.name,
333
- columnName: check.column_name,
334
- definition: `check (${expression})`,
335
- expression,
336
- });
337
- }
338
- return ret;
339
- }
340
- async loadInformationSchema(schema, connection, tables) {
341
- if (tables.length === 0) {
342
- return;
343
- }
344
- const tablesBySchema = this.getTablesGroupedBySchemas(tables);
345
- const columns = await this.getAllColumns(connection, tablesBySchema);
346
- const indexes = await this.getAllIndexes(connection, tablesBySchema);
347
- const checks = await this.getAllChecks(connection, tablesBySchema);
348
- const fks = await this.getAllForeignKeys(connection, tablesBySchema);
349
- for (const t of tables) {
350
- const key = this.getTableKey(t);
351
- const table = schema.addTable(t.table_name, t.schema_name, t.table_comment);
352
- const pks = await this.getPrimaryKeys(connection, indexes[key], table.name, table.schema);
353
- const enums = this.getEnumDefinitions(checks[key] ?? []);
354
- table.init(columns[key], indexes[key], checks[key], pks, fks[key], enums);
355
- }
356
- }
357
- getPreAlterTable(tableDiff, safe) {
358
- const ret = [];
359
- const indexes = tableDiff.fromTable.getIndexes();
360
- const parts = tableDiff.name.split('.');
361
- const tableName = parts.pop();
362
- const schemaName = parts.pop();
363
- const name = (schemaName && schemaName !== this.platform.getDefaultSchemaName() ? schemaName + '.' : '') + tableName;
364
- const quotedName = this.quote(name);
365
- // indexes need to be first dropped to be able to change a column type
366
- const changedTypes = Object.values(tableDiff.changedColumns).filter(col => col.changedProperties.has('type'));
367
- for (const col of changedTypes) {
368
- for (const index of indexes) {
369
- if (index.columnNames.includes(col.column.name)) {
370
- ret.push(this.getDropIndexSQL(name, index));
371
- }
372
- }
373
- }
374
- return ret;
375
- }
376
- getPostAlterTable(tableDiff, safe) {
377
- const ret = [];
378
- const indexes = tableDiff.fromTable.getIndexes();
379
- const parts = tableDiff.name.split('.');
380
- const tableName = parts.pop();
381
- const schemaName = parts.pop();
382
- const name = (schemaName && schemaName !== this.platform.getDefaultSchemaName() ? schemaName + '.' : '') + tableName;
383
- // indexes need to be first dropped to be able to change a column type
384
- const changedTypes = Object.values(tableDiff.changedColumns).filter(col => col.changedProperties.has('type'));
385
- for (const col of changedTypes) {
386
- for (const index of indexes) {
387
- if (index.columnNames.includes(col.column.name)) {
388
- this.append(ret, this.getCreateIndexSQL(name, index));
389
- }
390
- }
391
- }
392
- return ret;
393
- }
394
- getCreateNamespaceSQL(name) {
395
- const rawPassword = this.platform.getConfig().get('password');
396
- /* v8 ignore start: password type and tableSpace fallback */
397
- const password = typeof rawPassword === 'string' ? rawPassword : 'Schema_' + Math.random().toString(36).slice(2);
398
- const tableSpace = this.platform.getConfig().get('schemaGenerator').tableSpace ?? 'users';
399
- /* v8 ignore stop */
400
- return [
401
- `create user ${this.quote(name)}`,
402
- `identified by ${this.quote(password)}`,
403
- `default tablespace ${this.quote(tableSpace)}`,
404
- `quota unlimited on ${this.quote(tableSpace)}`,
405
- ].join(' ');
406
- }
407
- getDropNamespaceSQL(name) {
408
- return `drop user ${this.quote(name)} cascade`;
409
- }
410
- getDropIndexSQL(tableName, index) {
411
- return `drop index ${this.quote(index.keyName)}`;
412
- }
413
- dropIndex(table, index, oldIndexName = index.keyName) {
414
- if (index.primary) {
415
- return `alter table ${this.quote(table)} drop constraint ${this.quote(oldIndexName)}`;
416
- }
417
- return `drop index ${this.quote(oldIndexName)}`;
418
- }
419
- getManagementDbName() {
420
- /* v8 ignore next: managementDbName fallback */
421
- return this.platform.getConfig().get('schemaGenerator', {}).managementDbName ?? 'system';
422
- }
423
- getDatabaseNotExistsError(dbName) {
424
- return 'ORA-01918';
425
- }
426
- getCreateDatabaseSQL(name) {
427
- return `create user ${this.quote(name)}`;
428
- }
429
- getDropDatabaseSQL(name) {
430
- return `drop user ${this.quote(name)} cascade`;
431
- }
432
- getDropColumnsSQL(tableName, columns, schemaName) {
433
- /* v8 ignore next 3: schema prefix branch */
434
- const tableNameRaw = this.quote((schemaName && schemaName !== this.platform.getDefaultSchemaName() ? schemaName + '.' : '') + tableName);
435
- const drops = [];
436
- for (const column of columns) {
437
- drops.push(this.quote(column.name));
438
- }
439
- return `alter table ${tableNameRaw} drop (${drops.join(', ')})`;
440
- }
441
- getRenameColumnSQL(tableName, oldColumnName, to, schemaName) {
442
- /* v8 ignore next 2: schema prefix branch */
443
- const tableNameRaw = (schemaName && schemaName !== this.platform.getDefaultSchemaName() ? schemaName + '.' : '') + tableName;
444
- return `alter table ${this.quote(tableNameRaw)} rename column ${this.quote(oldColumnName)} to ${this.quote(to.name)}`;
445
- }
446
- createTableColumn(column, table, changedProperties) {
447
- const compositePK = table.getPrimaryKey()?.composite;
448
- const primaryKey = !changedProperties && !this.hasNonDefaultPrimaryKeyName(table);
449
- /* v8 ignore next: generated column branch */
450
- const columnType = column.generated ? `as ${column.generated}` : column.type;
451
- const col = [this.quote(column.name), columnType];
452
- Utils.runIfNotEmpty(() => col.push('generated by default as identity'), column.autoincrement);
453
- /* v8 ignore next 3: default value branch */
454
- const useDefault = changedProperties
455
- ? false
456
- : column.default != null && column.default !== 'null' && !column.autoincrement;
457
- // const defaultName = this.platform.getConfig().getNamingStrategy().indexName(table.name, [column.name], 'default');
458
- Utils.runIfNotEmpty(() => col.push(`default ${column.default}`), useDefault);
459
- /* v8 ignore next 2: nullable/not-null branches */
460
- Utils.runIfNotEmpty(() => col.push('null'), column.nullable);
461
- Utils.runIfNotEmpty(() => col.push('not null'), !column.nullable && !column.generated);
462
- /* v8 ignore next 6: autoincrement PK branch depends on column diff state */
463
- if (column.autoincrement &&
464
- !column.generated &&
465
- !compositePK &&
466
- (!changedProperties || changedProperties.has('autoincrement') || changedProperties.has('type'))) {
467
- Utils.runIfNotEmpty(() => col.push('primary key'), primaryKey && column.primary);
468
- }
469
- return col.join(' ');
470
- }
471
- alterTableColumn(column, table, changedProperties) {
472
- const parts = [];
473
- const quotedTableName = table.getQuotedName();
474
- // Oracle uses MODIFY for column changes, and always requires the column type
475
- if (changedProperties.has('type') || changedProperties.has('nullable') || changedProperties.has('default')) {
476
- const colParts = [this.quote(column.name), column.type];
477
- if (changedProperties.has('default')) {
478
- if (column.default != null && column.default !== 'null') {
479
- colParts.push(`default ${column.default}`);
480
- }
481
- else {
482
- colParts.push('default null');
483
- }
484
- }
485
- if (changedProperties.has('nullable')) {
486
- colParts.push(column.nullable ? 'null' : 'not null');
487
- }
488
- parts.push(`alter table ${quotedTableName} modify ${colParts.join(' ')}`);
489
- }
490
- return parts;
491
- }
492
- getCreateIndexSQL(tableName, index, partialExpression = false) {
493
- if (index.expression && !partialExpression) {
494
- return index.expression;
495
- }
496
- const keyName = this.quote(index.keyName);
497
- /* v8 ignore next: deferred index branch */
498
- const defer = index.deferMode ? ` deferrable initially ${index.deferMode}` : '';
499
- const sql = `create ${index.unique ? 'unique ' : ''}index ${keyName} on ${this.quote(tableName)} `;
500
- if (index.expression && partialExpression) {
501
- return `${sql}(${index.expression})${defer}`;
502
- }
503
- return super.getCreateIndexSQL(tableName, index);
504
- }
505
- createIndex(index, table, createPrimary = false) {
506
- if (index.primary) {
507
- return '';
508
- }
509
- if (index.expression) {
510
- return index.expression;
511
- }
512
- // oracle creates an implicit index for unique constraints, so skip creating
513
- // a non-unique index when a unique index on the same columns already exists
514
- if (!index.unique) {
515
- const cols = index.columnNames.join(',');
516
- const hasUniqueIndex = table
517
- .getIndexes()
518
- .some(i => i.unique && i.keyName !== index.keyName && i.columnNames.join(',') === cols);
519
- if (hasUniqueIndex) {
520
- return '';
521
- }
522
- }
523
- const quotedTableName = table.getQuotedName();
524
- if (index.unique) {
525
- const nullableCols = index.columnNames.filter(column => table.getColumn(column)?.nullable);
526
- return `create unique index ${this.quote(index.keyName)} on ${quotedTableName} (${index.columnNames
527
- .map(c => {
528
- if (table.getColumn(c)?.nullable) {
529
- return `case when ${nullableCols.map(c => `${this.quote(c)} is not null`).join(' and ')} then ${this.quote(c)} end`;
530
- }
531
- return this.quote(c);
532
- })
533
- .join(', ')})`;
534
- }
535
- return super.createIndex(index, table);
536
- }
537
- dropForeignKey(tableName, constraintName) {
538
- return `alter table ${this.quote(tableName)} drop constraint ${this.quote(constraintName)}`;
539
- }
540
- dropViewIfExists(name, schema) {
541
- if (schema === this.platform.getDefaultSchemaName()) {
542
- schema = undefined;
543
- }
544
- return `drop view if exists ${this.quote(schema, name)} cascade constraints`;
545
- }
546
- dropTableIfExists(name, schema) {
547
- if (schema === this.platform.getDefaultSchemaName()) {
548
- schema = undefined;
549
- }
550
- return `drop table if exists ${this.quote(schema, name)} cascade constraint`;
551
- }
552
- getAddColumnsSQL(table, columns) {
553
- const adds = columns
554
- .map(column => {
555
- return this.createTableColumn(column, table);
326
+ }
327
+ async getAllChecks(connection, tablesBySchemas) {
328
+ const sql = this.getChecksSQL(tablesBySchemas);
329
+ const allChecks = await connection.execute(sql);
330
+ const ret = {};
331
+ for (const check of allChecks) {
332
+ const key = this.getTableKey(check);
333
+ ret[key] ??= [];
334
+ const expression = check.expression.replace(/^\((.*)\)$/, '$1');
335
+ ret[key].push({
336
+ name: check.name,
337
+ columnName: check.column_name,
338
+ definition: `check (${expression})`,
339
+ expression,
340
+ });
341
+ }
342
+ return ret;
343
+ }
344
+ async loadInformationSchema(schema, connection, tables) {
345
+ if (tables.length === 0) {
346
+ return;
347
+ }
348
+ const tablesBySchema = this.getTablesGroupedBySchemas(tables);
349
+ const columns = await this.getAllColumns(connection, tablesBySchema);
350
+ const indexes = await this.getAllIndexes(connection, tablesBySchema);
351
+ const checks = await this.getAllChecks(connection, tablesBySchema);
352
+ const fks = await this.getAllForeignKeys(connection, tablesBySchema);
353
+ for (const t of tables) {
354
+ const key = this.getTableKey(t);
355
+ const table = schema.addTable(t.table_name, t.schema_name, t.table_comment);
356
+ const pks = await this.getPrimaryKeys(connection, indexes[key], table.name, table.schema);
357
+ const enums = this.getEnumDefinitions(checks[key] ?? []);
358
+ table.init(columns[key], indexes[key], checks[key], pks, fks[key], enums);
359
+ }
360
+ }
361
+ getPreAlterTable(tableDiff, safe) {
362
+ const ret = [];
363
+ const indexes = tableDiff.fromTable.getIndexes();
364
+ const parts = tableDiff.name.split('.');
365
+ const tableName = parts.pop();
366
+ const schemaName = parts.pop();
367
+ const name =
368
+ (schemaName && schemaName !== this.platform.getDefaultSchemaName() ? schemaName + '.' : '') + tableName;
369
+ const quotedName = this.quote(name);
370
+ // indexes need to be first dropped to be able to change a column type
371
+ const changedTypes = Object.values(tableDiff.changedColumns).filter(col => col.changedProperties.has('type'));
372
+ for (const col of changedTypes) {
373
+ for (const index of indexes) {
374
+ if (index.columnNames.includes(col.column.name)) {
375
+ ret.push(this.getDropIndexSQL(name, index));
376
+ }
377
+ }
378
+ }
379
+ return ret;
380
+ }
381
+ getPostAlterTable(tableDiff, safe) {
382
+ const ret = [];
383
+ const indexes = tableDiff.fromTable.getIndexes();
384
+ const parts = tableDiff.name.split('.');
385
+ const tableName = parts.pop();
386
+ const schemaName = parts.pop();
387
+ const name =
388
+ (schemaName && schemaName !== this.platform.getDefaultSchemaName() ? schemaName + '.' : '') + tableName;
389
+ // indexes need to be first dropped to be able to change a column type
390
+ const changedTypes = Object.values(tableDiff.changedColumns).filter(col => col.changedProperties.has('type'));
391
+ for (const col of changedTypes) {
392
+ for (const index of indexes) {
393
+ if (index.columnNames.includes(col.column.name)) {
394
+ this.append(ret, this.getCreateIndexSQL(name, index));
395
+ }
396
+ }
397
+ }
398
+ return ret;
399
+ }
400
+ getCreateNamespaceSQL(name) {
401
+ const rawPassword = this.platform.getConfig().get('password');
402
+ /* v8 ignore start: password type and tableSpace fallback */
403
+ const password = typeof rawPassword === 'string' ? rawPassword : 'Schema_' + Math.random().toString(36).slice(2);
404
+ const tableSpace = this.platform.getConfig().get('schemaGenerator').tableSpace ?? 'users';
405
+ /* v8 ignore stop */
406
+ return [
407
+ `create user ${this.quote(name)}`,
408
+ `identified by ${this.quote(password)}`,
409
+ `default tablespace ${this.quote(tableSpace)}`,
410
+ `quota unlimited on ${this.quote(tableSpace)}`,
411
+ ].join(' ');
412
+ }
413
+ getDropNamespaceSQL(name) {
414
+ return `drop user ${this.quote(name)} cascade`;
415
+ }
416
+ getDropIndexSQL(tableName, index) {
417
+ return `drop index ${this.quote(index.keyName)}`;
418
+ }
419
+ dropIndex(table, index, oldIndexName = index.keyName) {
420
+ if (index.primary) {
421
+ return `alter table ${this.quote(table)} drop constraint ${this.quote(oldIndexName)}`;
422
+ }
423
+ return `drop index ${this.quote(oldIndexName)}`;
424
+ }
425
+ getManagementDbName() {
426
+ /* v8 ignore next: managementDbName fallback */
427
+ return this.platform.getConfig().get('schemaGenerator', {}).managementDbName ?? 'system';
428
+ }
429
+ getDatabaseNotExistsError(dbName) {
430
+ return 'ORA-01918';
431
+ }
432
+ getCreateDatabaseSQL(name) {
433
+ return `create user ${this.quote(name)}`;
434
+ }
435
+ getDropDatabaseSQL(name) {
436
+ return `drop user ${this.quote(name)} cascade`;
437
+ }
438
+ getDropColumnsSQL(tableName, columns, schemaName) {
439
+ /* v8 ignore next 3: schema prefix branch */
440
+ const tableNameRaw = this.quote(
441
+ (schemaName && schemaName !== this.platform.getDefaultSchemaName() ? schemaName + '.' : '') + tableName,
442
+ );
443
+ const drops = [];
444
+ for (const column of columns) {
445
+ drops.push(this.quote(column.name));
446
+ }
447
+ return `alter table ${tableNameRaw} drop (${drops.join(', ')})`;
448
+ }
449
+ getRenameColumnSQL(tableName, oldColumnName, to, schemaName) {
450
+ /* v8 ignore next 2: schema prefix branch */
451
+ const tableNameRaw =
452
+ (schemaName && schemaName !== this.platform.getDefaultSchemaName() ? schemaName + '.' : '') + tableName;
453
+ return `alter table ${this.quote(tableNameRaw)} rename column ${this.quote(oldColumnName)} to ${this.quote(to.name)}`;
454
+ }
455
+ createTableColumn(column, table, changedProperties) {
456
+ const compositePK = table.getPrimaryKey()?.composite;
457
+ const primaryKey = !changedProperties && !this.hasNonDefaultPrimaryKeyName(table);
458
+ /* v8 ignore next: generated column branch */
459
+ const columnType = column.generated ? `as ${column.generated}` : column.type;
460
+ const col = [this.quote(column.name), columnType];
461
+ Utils.runIfNotEmpty(() => col.push('generated by default as identity'), column.autoincrement);
462
+ /* v8 ignore next 3: default value branch */
463
+ const useDefault = changedProperties
464
+ ? false
465
+ : column.default != null && column.default !== 'null' && !column.autoincrement;
466
+ // const defaultName = this.platform.getConfig().getNamingStrategy().indexName(table.name, [column.name], 'default');
467
+ Utils.runIfNotEmpty(() => col.push(`default ${column.default}`), useDefault);
468
+ /* v8 ignore next 2: nullable/not-null branches */
469
+ Utils.runIfNotEmpty(() => col.push('null'), column.nullable);
470
+ Utils.runIfNotEmpty(() => col.push('not null'), !column.nullable && !column.generated);
471
+ /* v8 ignore next 6: autoincrement PK branch depends on column diff state */
472
+ if (
473
+ column.autoincrement &&
474
+ !column.generated &&
475
+ !compositePK &&
476
+ (!changedProperties || changedProperties.has('autoincrement') || changedProperties.has('type'))
477
+ ) {
478
+ Utils.runIfNotEmpty(() => col.push('primary key'), primaryKey && column.primary);
479
+ }
480
+ return col.join(' ');
481
+ }
482
+ alterTableColumn(column, table, changedProperties) {
483
+ const parts = [];
484
+ const quotedTableName = table.getQuotedName();
485
+ // Oracle uses MODIFY for column changes, and always requires the column type
486
+ if (changedProperties.has('type') || changedProperties.has('nullable') || changedProperties.has('default')) {
487
+ const colParts = [this.quote(column.name), column.type];
488
+ if (changedProperties.has('default')) {
489
+ if (column.default != null && column.default !== 'null') {
490
+ colParts.push(`default ${column.default}`);
491
+ } else {
492
+ colParts.push('default null');
493
+ }
494
+ }
495
+ if (changedProperties.has('nullable')) {
496
+ colParts.push(column.nullable ? 'null' : 'not null');
497
+ }
498
+ parts.push(`alter table ${quotedTableName} modify ${colParts.join(' ')}`);
499
+ }
500
+ return parts;
501
+ }
502
+ getCreateIndexSQL(tableName, index, partialExpression = false) {
503
+ if (index.expression && !partialExpression) {
504
+ return index.expression;
505
+ }
506
+ const keyName = this.quote(index.keyName);
507
+ /* v8 ignore next: deferred index branch */
508
+ const defer = index.deferMode ? ` deferrable initially ${index.deferMode}` : '';
509
+ const sql = `create ${index.unique ? 'unique ' : ''}index ${keyName} on ${this.quote(tableName)} `;
510
+ if (index.expression && partialExpression) {
511
+ return `${sql}(${index.expression})${defer}`;
512
+ }
513
+ return super.getCreateIndexSQL(tableName, index);
514
+ }
515
+ createIndex(index, table, createPrimary = false) {
516
+ if (index.primary) {
517
+ return '';
518
+ }
519
+ if (index.expression) {
520
+ return index.expression;
521
+ }
522
+ // oracle creates an implicit index for unique constraints, so skip creating
523
+ // a non-unique index when a unique index on the same columns already exists
524
+ if (!index.unique) {
525
+ const cols = index.columnNames.join(',');
526
+ const hasUniqueIndex = table
527
+ .getIndexes()
528
+ .some(i => i.unique && i.keyName !== index.keyName && i.columnNames.join(',') === cols);
529
+ if (hasUniqueIndex) {
530
+ return '';
531
+ }
532
+ }
533
+ const quotedTableName = table.getQuotedName();
534
+ if (index.unique) {
535
+ const nullableCols = index.columnNames.filter(column => table.getColumn(column)?.nullable);
536
+ return `create unique index ${this.quote(index.keyName)} on ${quotedTableName} (${index.columnNames
537
+ .map(c => {
538
+ if (table.getColumn(c)?.nullable) {
539
+ return `case when ${nullableCols.map(c => `${this.quote(c)} is not null`).join(' and ')} then ${this.quote(c)} end`;
540
+ }
541
+ return this.quote(c);
556
542
  })
557
- .join(', ');
558
- // Oracle requires parentheses when adding multiple columns
559
- const wrap = columns.length > 1 ? `(${adds})` : adds;
560
- return [`alter table ${table.getQuotedName()} add ${wrap}`];
561
- }
562
- appendComments(table) {
563
- const sql = [];
564
- if (table.comment) {
565
- const comment = this.platform.quoteValue(this.processComment(table.comment));
566
- sql.push(`comment on table ${table.getQuotedName()} is ${comment}`);
567
- }
568
- for (const column of table.getColumns()) {
569
- if (column.comment) {
570
- const comment = this.platform.quoteValue(this.processComment(column.comment));
571
- sql.push(`comment on column ${table.getQuotedName()}.${this.quote(column.name)} is ${comment}`);
572
- }
573
- }
574
- return sql;
575
- }
576
- inferLengthFromColumnType(type) {
577
- const match = /^(\w+)\s*\(\s*(-?\d+|max)\s*\)/.exec(type);
578
- if (!match) {
579
- return;
580
- }
581
- if (match[2] === 'max') {
582
- return -1;
583
- }
584
- return +match[2];
585
- }
586
- /* v8 ignore next 4: wrap is called by schema comparator internals */
587
- wrap(val, type) {
588
- const stringType = type instanceof StringType || type instanceof TextType || type instanceof EnumType;
589
- return typeof val === 'string' && val.length > 0 && stringType ? this.platform.quoteValue(val) : val;
590
- }
543
+ .join(', ')})`;
544
+ }
545
+ return super.createIndex(index, table);
546
+ }
547
+ dropForeignKey(tableName, constraintName) {
548
+ return `alter table ${this.quote(tableName)} drop constraint ${this.quote(constraintName)}`;
549
+ }
550
+ dropViewIfExists(name, schema) {
551
+ if (schema === this.platform.getDefaultSchemaName()) {
552
+ schema = undefined;
553
+ }
554
+ return `drop view if exists ${this.quote(schema, name)} cascade constraints`;
555
+ }
556
+ dropTableIfExists(name, schema) {
557
+ if (schema === this.platform.getDefaultSchemaName()) {
558
+ schema = undefined;
559
+ }
560
+ return `drop table if exists ${this.quote(schema, name)} cascade constraint`;
561
+ }
562
+ getAddColumnsSQL(table, columns) {
563
+ const adds = columns
564
+ .map(column => {
565
+ return this.createTableColumn(column, table);
566
+ })
567
+ .join(', ');
568
+ // Oracle requires parentheses when adding multiple columns
569
+ const wrap = columns.length > 1 ? `(${adds})` : adds;
570
+ return [`alter table ${table.getQuotedName()} add ${wrap}`];
571
+ }
572
+ appendComments(table) {
573
+ const sql = [];
574
+ if (table.comment) {
575
+ const comment = this.platform.quoteValue(this.processComment(table.comment));
576
+ sql.push(`comment on table ${table.getQuotedName()} is ${comment}`);
577
+ }
578
+ for (const column of table.getColumns()) {
579
+ if (column.comment) {
580
+ const comment = this.platform.quoteValue(this.processComment(column.comment));
581
+ sql.push(`comment on column ${table.getQuotedName()}.${this.quote(column.name)} is ${comment}`);
582
+ }
583
+ }
584
+ return sql;
585
+ }
586
+ inferLengthFromColumnType(type) {
587
+ const match = /^(\w+)\s*\(\s*(-?\d+|max)\s*\)/.exec(type);
588
+ if (!match) {
589
+ return;
590
+ }
591
+ if (match[2] === 'max') {
592
+ return -1;
593
+ }
594
+ return +match[2];
595
+ }
596
+ /* v8 ignore next 4: wrap is called by schema comparator internals */
597
+ wrap(val, type) {
598
+ const stringType = type instanceof StringType || type instanceof TextType || type instanceof EnumType;
599
+ return typeof val === 'string' && val.length > 0 && stringType ? this.platform.quoteValue(val) : val;
600
+ }
591
601
  }