Merge branch 'new-oracle-driver'

This commit is contained in:
Jan Prochazka
2024-06-01 14:38:01 +02:00
56 changed files with 415 additions and 517 deletions

View File

@@ -22,7 +22,7 @@ DbGate is licensed under MIT license and is completely free.
* MySQL * MySQL
* PostgreSQL * PostgreSQL
* SQL Server * SQL Server
* Oracle (experimental) * Oracle
* MongoDB * MongoDB
* Redis * Redis
* SQLite * SQLite

View File

@@ -5,9 +5,6 @@ function adjustFile(file) {
if (process.platform != 'win32') { if (process.platform != 'win32') {
delete json.optionalDependencies.msnodesqlv8; delete json.optionalDependencies.msnodesqlv8;
} }
if (process.arch == 'arm64') {
delete json.optionalDependencies.oracledb;
}
fs.writeFileSync(file, JSON.stringify(json, null, 2), 'utf-8'); fs.writeFileSync(file, JSON.stringify(json, null, 2), 'utf-8');
} }

View File

@@ -121,7 +121,6 @@
}, },
"optionalDependencies": { "optionalDependencies": {
"better-sqlite3": "9.6.0", "better-sqlite3": "9.6.0",
"msnodesqlv8": "^4.2.1", "msnodesqlv8": "^4.2.1"
"oracledb": "^5.5.0"
} }
} }

View File

@@ -1944,11 +1944,6 @@ open@^7.4.2:
is-docker "^2.0.0" is-docker "^2.0.0"
is-wsl "^2.1.1" is-wsl "^2.1.1"
oracledb@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/oracledb/-/oracledb-5.5.0.tgz#0cf9af5d0c0815f74849ae9ed56aee823514d71b"
integrity sha512-i5cPvMENpZP8nnqptB6l0pjiOyySj1IISkbM4Hr3yZEDdANo2eezarwZb9NQ8fTh5pRjmgpZdSyIbnn9N3AENw==
os-tmpdir@~1.0.2: os-tmpdir@~1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"

View File

@@ -5,9 +5,6 @@ let fillContent = '';
if (process.platform == 'win32') { if (process.platform == 'win32') {
fillContent += `content.msnodesqlv8 = () => require('msnodesqlv8');`; fillContent += `content.msnodesqlv8 = () => require('msnodesqlv8');`;
} }
if (process.arch != 'arm64') {
fillContent += `content.oracledb = () => require('oracledb');`;
}
fillContent += `content['better-sqlite3'] = () => require('better-sqlite3');`; fillContent += `content['better-sqlite3'] = () => require('better-sqlite3');`;
const getContent = empty => ` const getContent = empty => `

View File

@@ -26,10 +26,10 @@
"compare-versions": "^3.6.0", "compare-versions": "^3.6.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"cross-env": "^6.0.3", "cross-env": "^6.0.3",
"dbgate-query-splitter": "^4.9.3", "dbgate-datalib": "^5.0.0-alpha.1",
"dbgate-query-splitter": "^4.10.1",
"dbgate-sqltree": "^5.0.0-alpha.1", "dbgate-sqltree": "^5.0.0-alpha.1",
"dbgate-tools": "^5.0.0-alpha.1", "dbgate-tools": "^5.0.0-alpha.1",
"dbgate-datalib": "^5.0.0-alpha.1",
"debug": "^4.3.4", "debug": "^4.3.4",
"diff": "^5.0.0", "diff": "^5.0.0",
"diff2html": "^3.4.13", "diff2html": "^3.4.13",
@@ -83,7 +83,6 @@
}, },
"optionalDependencies": { "optionalDependencies": {
"better-sqlite3": "9.6.0", "better-sqlite3": "9.6.0",
"msnodesqlv8": "^4.2.1", "msnodesqlv8": "^4.2.1"
"oracledb": "^5.5.0"
} }
} }

View File

@@ -61,6 +61,7 @@ function getPortalCollections() {
useDatabaseUrl: !!process.env[`URL_${id}`], useDatabaseUrl: !!process.env[`URL_${id}`],
databaseFile: process.env[`FILE_${id}`], databaseFile: process.env[`FILE_${id}`],
socketPath: process.env[`SOCKET_PATH_${id}`], socketPath: process.env[`SOCKET_PATH_${id}`],
serviceName: process.env[`SERVICE_NAME_${id}`],
authType: process.env[`AUTH_TYPE_${id}`] || (process.env[`SOCKET_PATH_${id}`] ? 'socket' : undefined), authType: process.env[`AUTH_TYPE_${id}`] || (process.env[`SOCKET_PATH_${id}`] ? 'socket' : undefined),
defaultDatabase: defaultDatabase:
process.env[`DATABASE_${id}`] || process.env[`DATABASE_${id}`] ||

View File

@@ -31,7 +31,7 @@
"typescript": "^4.4.3" "typescript": "^4.4.3"
}, },
"dependencies": { "dependencies": {
"dbgate-query-splitter": "^4.9.3", "dbgate-query-splitter": "^4.10.1",
"dbgate-sqltree": "^5.0.0-alpha.1", "dbgate-sqltree": "^5.0.0-alpha.1",
"debug": "^4.3.4", "debug": "^4.3.4",
"json-stable-stringify": "^1.0.1", "json-stable-stringify": "^1.0.1",

View File

@@ -180,8 +180,15 @@ export class DatabaseAnalyser {
// return this.createQueryCore('=OBJECT_ID_CONDITION', typeFields) != ' is not null'; // return this.createQueryCore('=OBJECT_ID_CONDITION', typeFields) != ' is not null';
// } // }
createQuery(template, typeFields) { createQuery(template, typeFields, replacements = {}) {
return this.createQueryCore(template, typeFields); return this.createQueryCore(this.processQueryReplacements(template, replacements), typeFields);
}
processQueryReplacements(query, replacements) {
for (const repl in replacements) {
query = query.replaceAll(repl, replacements[repl]);
}
return query;
} }
createQueryCore(template, typeFields) { createQueryCore(template, typeFields) {
@@ -302,8 +309,8 @@ export class DatabaseAnalyser {
return [..._compact(res), ...this.getDeletedObjects(snapshot)]; return [..._compact(res), ...this.getDeletedObjects(snapshot)];
} }
async analyserQuery(template, typeFields) { async analyserQuery(template, typeFields, replacements = {}) {
const sql = this.createQuery(template, typeFields); const sql = this.createQuery(template, typeFields, replacements);
if (!sql) { if (!sql) {
return { return {
@@ -311,7 +318,9 @@ export class DatabaseAnalyser {
}; };
} }
try { try {
return await this.driver.query(this.pool, sql); const res = await this.driver.query(this.pool, sql);
this.logger.debug({ rows: res.rows.length, template }, `Loaded analyser query`);
return res;
} catch (err) { } catch (err) {
logger.error({ err }, 'Error running analyser query'); logger.error({ err }, 'Error running analyser query');
return { return {

View File

@@ -199,14 +199,8 @@ export class SqlDumper implements AlterProcessor {
selectScopeIdentity(table: TableInfo) {} selectScopeIdentity(table: TableInfo) {}
columnDefinition(column: ColumnInfo, { includeDefault = true, includeNullable = true, includeCollate = true } = {}) { columnType(dataType: string) {
if (column.computedExpression) { const type = dataType || this.dialect.fallbackDataType;
this.put('^as %s', column.computedExpression);
if (column.isPersisted) this.put(' ^persisted');
return;
}
const type = column.dataType || this.dialect.fallbackDataType;
const typeWithValues = type.match(/([^(]+)(\(.+[^)]\))/); const typeWithValues = type.match(/([^(]+)(\(.+[^)]\))/);
if (typeWithValues?.length) { if (typeWithValues?.length) {
@@ -217,6 +211,17 @@ export class SqlDumper implements AlterProcessor {
this.putRaw(SqlDumper.convertKeywordCase(type)); this.putRaw(SqlDumper.convertKeywordCase(type));
} }
}
columnDefinition(column: ColumnInfo, { includeDefault = true, includeNullable = true, includeCollate = true } = {}) {
if (column.computedExpression) {
this.put('^as %s', column.computedExpression);
if (column.isPersisted) this.put(' ^persisted');
return;
}
this.columnType(column.dataType);
if (column.autoIncrement) { if (column.autoIncrement) {
this.autoIncrement(); this.autoIncrement();
} }

View File

@@ -56,24 +56,42 @@ export function createBulkInsertStreamBase(driver: EngineDriver, stream, pool, n
const rows = writable.buffer; const rows = writable.buffer;
writable.buffer = []; writable.buffer = [];
const dmp = driver.createDumper(); if (driver.dialect.allowMultipleValuesInsert) {
const dmp = driver.createDumper();
dmp.putRaw(`INSERT INTO ${fullNameQuoted} (`);
dmp.putCollection(',', writable.columnNames, col => dmp.putRaw(driver.dialect.quoteIdentifier(col as string)));
dmp.putRaw(')\n VALUES\n');
dmp.putRaw(`INSERT INTO ${fullNameQuoted} (`); let wasRow = false;
dmp.putCollection(',', writable.columnNames, col => dmp.putRaw(driver.dialect.quoteIdentifier(col as string))); for (const row of rows) {
dmp.putRaw(')\n VALUES\n'); if (wasRow) dmp.putRaw(',\n');
dmp.putRaw('(');
dmp.putCollection(',', writable.columnNames, col => dmp.putValue(row[col as string]));
dmp.putRaw(')');
wasRow = true;
}
dmp.putRaw(';');
// require('fs').writeFileSync('/home/jena/test.sql', dmp.s);
// console.log(dmp.s);
await driver.query(pool, dmp.s, { discardResult: true });
} else {
for (const row of rows) {
const dmp = driver.createDumper();
dmp.putRaw(`INSERT INTO ${fullNameQuoted} (`);
dmp.putCollection(',', writable.columnNames, col => dmp.putRaw(driver.dialect.quoteIdentifier(col as string)));
dmp.putRaw(')\n VALUES\n');
let wasRow = false; dmp.putRaw('(');
for (const row of rows) { dmp.putCollection(',', writable.columnNames, col => dmp.putValue(row[col as string]));
if (wasRow) dmp.putRaw(',\n'); dmp.putRaw(')');
dmp.putRaw('('); await driver.query(pool, dmp.s, { discardResult: true });
dmp.putCollection(',', writable.columnNames, col => dmp.putValue(row[col as string])); }
dmp.putRaw(')'); }
wasRow = true; if (options.commitAfterInsert) {
const dmp = driver.createDumper();
dmp.commitTransaction();
await driver.query(pool, dmp.s, { discardResult: true });
} }
dmp.putRaw(';');
// require('fs').writeFileSync('/home/jena/test.sql', dmp.s);
// console.log(dmp.s);
await driver.query(pool, dmp.s, { discardResult: true });
}; };
writable.sendIfFull = async () => { writable.sendIfFull = async () => {

View File

@@ -12,6 +12,7 @@ export interface SqlDialect {
defaultSchemaName?: string; defaultSchemaName?: string;
enableConstraintsPerTable?: boolean; enableConstraintsPerTable?: boolean;
requireStandaloneSelectForScopeIdentity?: boolean; requireStandaloneSelectForScopeIdentity?: boolean;
allowMultipleValuesInsert?: boolean;
dropColumnDependencies?: string[]; dropColumnDependencies?: string[];
changeColumnDependencies?: string[]; changeColumnDependencies?: string[];

View File

@@ -24,6 +24,7 @@ export interface WriteTableOptions {
dropIfExists?: boolean; dropIfExists?: boolean;
truncate?: boolean; truncate?: boolean;
createIfNotExists?: boolean; createIfNotExists?: boolean;
commitAfterInsert?: boolean;
} }
export interface EngineAuthType { export interface EngineAuthType {

View File

@@ -24,7 +24,7 @@
"chartjs-adapter-moment": "^1.0.0", "chartjs-adapter-moment": "^1.0.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"dbgate-datalib": "^5.0.0-alpha.1", "dbgate-datalib": "^5.0.0-alpha.1",
"dbgate-query-splitter": "^4.9.3", "dbgate-query-splitter": "^4.10.1",
"dbgate-sqltree": "^5.0.0-alpha.1", "dbgate-sqltree": "^5.0.0-alpha.1",
"dbgate-tools": "^5.0.0-alpha.1", "dbgate-tools": "^5.0.0-alpha.1",
"dbgate-types": "^5.0.0-alpha.1", "dbgate-types": "^5.0.0-alpha.1",

View File

@@ -123,6 +123,10 @@
{/if} {/if}
{/if} {/if}
{#if driver?.showConnectionField('serviceName', $values)}
<FormTextField label="Service name" name="serviceName" disabled={isConnected} />
{/if}
{#if driver?.showConnectionField('socketPath', $values)} {#if driver?.showConnectionField('socketPath', $values)}
<FormTextField <FormTextField
label="Socket path" label="Socket path"

View File

@@ -84,6 +84,7 @@
'defaultDatabase', 'defaultDatabase',
'singleDatabase', 'singleDatabase',
'socketPath', 'socketPath',
'serviceName',
]; ];
const visibleProps = allProps.filter(x => driver?.showConnectionField(x, $values)); const visibleProps = allProps.filter(x => driver?.showConnectionField(x, $values));
const omitProps = _.difference(allProps, visibleProps); const omitProps = _.difference(allProps, visibleProps);

View File

@@ -24,6 +24,9 @@ function getConnectionLabelCore(connection, { allowExplicitDatabase = true } = {
if (connection.singleDatabase && connection.defaultDatabase) { if (connection.singleDatabase && connection.defaultDatabase) {
return `${connection.defaultDatabase}`; return `${connection.defaultDatabase}`;
} }
if (connection.useDatabaseUrl) {
return `${connection.databaseUrl}`;
}
return ''; return '';
} }

View File

@@ -32,7 +32,7 @@
}, },
"devDependencies": { "devDependencies": {
"dbgate-plugin-tools": "^1.0.7", "dbgate-plugin-tools": "^1.0.7",
"dbgate-query-splitter": "^4.9.3", "dbgate-query-splitter": "^4.10.1",
"webpack": "^5.91.0", "webpack": "^5.91.0",
"webpack-cli": "^5.1.4", "webpack-cli": "^5.1.4",
"dbgate-tools": "^5.0.0-alpha.1", "dbgate-tools": "^5.0.0-alpha.1",

View File

@@ -32,7 +32,7 @@
}, },
"devDependencies": { "devDependencies": {
"dbgate-plugin-tools": "^1.0.7", "dbgate-plugin-tools": "^1.0.7",
"dbgate-query-splitter": "^4.9.3", "dbgate-query-splitter": "^4.10.1",
"webpack": "^5.91.0", "webpack": "^5.91.0",
"webpack-cli": "^5.1.4", "webpack-cli": "^5.1.4",
"dbgate-tools": "^5.0.0-alpha.1", "dbgate-tools": "^5.0.0-alpha.1",

View File

@@ -33,7 +33,7 @@
"devDependencies": { "devDependencies": {
"antares-mysql-dumper": "^0.0.1", "antares-mysql-dumper": "^0.0.1",
"dbgate-plugin-tools": "^1.0.7", "dbgate-plugin-tools": "^1.0.7",
"dbgate-query-splitter": "^4.9.3", "dbgate-query-splitter": "^4.10.1",
"dbgate-tools": "^5.0.0-alpha.1", "dbgate-tools": "^5.0.0-alpha.1",
"mysql2": "^3.9.7", "mysql2": "^3.9.7",
"webpack": "^5.91.0", "webpack": "^5.91.0",

View File

@@ -22,6 +22,7 @@ const dialect = {
enableConstraintsPerTable: false, enableConstraintsPerTable: false,
anonymousPrimaryKey: true, anonymousPrimaryKey: true,
explicitDropConstraint: true, explicitDropConstraint: true,
allowMultipleValuesInsert: true,
quoteIdentifier(s) { quoteIdentifier(s) {
return '`' + s + '`'; return '`' + s + '`';
}, },

View File

@@ -31,10 +31,13 @@
}, },
"devDependencies": { "devDependencies": {
"dbgate-plugin-tools": "^1.0.8", "dbgate-plugin-tools": "^1.0.8",
"dbgate-query-splitter": "^4.9.0", "dbgate-query-splitter": "^4.10.1",
"dbgate-tools": "^5.0.0-alpha.1", "dbgate-tools": "^5.0.0-alpha.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"webpack": "^5.91.0", "webpack": "^5.91.0",
"webpack-cli": "^5.1.4" "webpack-cli": "^5.1.4"
},
"dependencies": {
"oracledb": "^6.5.1"
} }
} }

View File

@@ -13,38 +13,18 @@ function normalizeTypeName(dataType) {
function getColumnInfo( function getColumnInfo(
{ is_nullable, column_name, data_type, char_max_length, numeric_precision, numeric_ccale, default_value }, { is_nullable, column_name, data_type, char_max_length, numeric_precision, numeric_ccale, default_value },
table = undefined, table = undefined
geometryColumns = undefined,
geographyColumns = undefined
) { ) {
const normDataType = normalizeTypeName(data_type); const normDataType = normalizeTypeName(data_type);
let fullDataType = normDataType; let fullDataType = normDataType;
if (char_max_length && isTypeString(normDataType)) fullDataType = `${normDataType}(${char_max_length})`; if (char_max_length && isTypeString(normDataType)) fullDataType = `${normDataType}(${char_max_length})`;
if (numeric_precision && numeric_ccale && isTypeNumeric(normDataType)) if (numeric_precision && numeric_ccale && isTypeNumeric(normDataType))
fullDataType = `${normDataType}(${numeric_precision},${numeric_ccale})`; fullDataType = `${normDataType}(${numeric_precision},${numeric_ccale})`;
const autoIncrement = !!(default_value && default_value.startsWith('nextval(')); const autoIncrement = !!(default_value && default_value.endsWith('.nextval'));
if (
table &&
geometryColumns &&
geometryColumns.rows.find(
x => x.schema_name == table.schemaName && x.pure_name == table.pureName && x.column_name == column_name
)
) {
fullDataType = 'geometry';
}
if (
table &&
geographyColumns &&
geographyColumns.rows.find(
x => x.schema_name == table.schemaName && x.pure_name == table.pureName && x.column_name == column_name
)
) {
fullDataType = 'geography';
}
return { return {
columnName: column_name, columnName: column_name,
dataType: fullDataType, dataType: fullDataType,
notNull: !is_nullable || is_nullable == 'NO' || is_nullable == 'no', notNull: is_nullable == 'N',
defaultValue: autoIncrement ? undefined : default_value, defaultValue: autoIncrement ? undefined : default_value,
autoIncrement, autoIncrement,
}; };
@@ -55,58 +35,50 @@ class Analyser extends DatabaseAnalyser {
super(pool, driver, version); super(pool, driver, version);
} }
createQuery(resFileName, typeFields) { createQuery(resFileName, typeFields, replacements = {}) {
const query = super.createQuery(sql[resFileName], typeFields); const query = super.createQuery(sql[resFileName], typeFields, replacements);
//if (query) return query.replace('#REFTABLECOND#', this.driver.__analyserInternals.refTableCond); //if (query) return query.replace('#REFTABLECOND#', this.driver.__analyserInternals.refTableCond);
return query; return query;
} }
async _computeSingleObjectId() { async _computeSingleObjectId() {
const { typeField, schemaName, pureName } = this.singleObjectFilter; const { typeField, pureName } = this.singleObjectFilter;
this.singleObjectId = `${typeField}:${schemaName || 'public'}.${pureName}`; this.singleObjectId = `${typeField}:${pureName}`;
} }
async _runAnalysis() { async _runAnalysis() {
this.feedback({ analysingMessage: 'Loading tables' }); this.feedback({ analysingMessage: 'Loading tables' });
const tables = await this.analyserQuery(this.driver.dialect.stringAgg ? 'tableList' : 'tableList', ['tables']); const tables = await this.analyserQuery('tableList', ['tables'], { $owner: this.pool._schema_name });
this.feedback({ analysingMessage: 'Loading columns' }); this.feedback({ analysingMessage: 'Loading columns' });
const columns = await this.analyserQuery('columns', ['tables', 'views']); const columns = await this.analyserQuery('columns', ['tables', 'views'], { $owner: this.pool._schema_name });
this.feedback({ analysingMessage: 'Loading primary keys' }); this.feedback({ analysingMessage: 'Loading primary keys' });
const pkColumns = await this.analyserQuery('primaryKeys', ['tables']); const pkColumns = await this.analyserQuery('primaryKeys', ['tables'], { $owner: this.pool._schema_name });
//let fkColumns = null; //let fkColumns = null;
this.feedback({ analysingMessage: 'Loading foreign keys' }); this.feedback({ analysingMessage: 'Loading foreign keys' });
const fkColumns = await this.analyserQuery('foreignKeys', ['tables']); const fkColumns = await this.analyserQuery('foreignKeys', ['tables'], { $owner: this.pool._schema_name });
this.feedback({ analysingMessage: 'Loading views' }); this.feedback({ analysingMessage: 'Loading views' });
const views = await this.analyserQuery('views', ['views']); const views = await this.analyserQuery('views', ['views'], { $owner: this.pool._schema_name });
let geometryColumns = { rows: [] };
let geographyColumns = { rows: [] };
this.feedback({ analysingMessage: 'Loading materialized views' }); this.feedback({ analysingMessage: 'Loading materialized views' });
const matviews = this.driver.dialect.materializedViews ? await this.analyserQuery('matviews', ['matviews']) : null; const matviews = this.driver.dialect.materializedViews
this.feedback({ analysingMessage: 'Loading materialized view columns' }); ? await this.analyserQuery('matviews', ['matviews'], { $owner: this.pool._schema_name })
const matviewColumns = this.driver.dialect.materializedViews
? await this.analyserQuery('matviewColumns', ['matviews'])
: null; : null;
this.feedback({ analysingMessage: 'Loading routines' }); this.feedback({ analysingMessage: 'Loading routines' });
const routines = await this.analyserQuery('routines', ['procedures', 'functions']); const routines = await this.analyserQuery('routines', ['procedures', 'functions'], {
$owner: this.pool._schema_name,
});
this.feedback({ analysingMessage: 'Loading indexes' }); this.feedback({ analysingMessage: 'Loading indexes' });
const indexes = this.driver.__analyserInternals.skipIndexes const indexes = await this.analyserQuery('indexes', ['tables'], { $owner: this.pool._schema_name });
? { rows: [] }
: await this.analyserQuery('indexes', ['tables']);
this.feedback({ analysingMessage: 'Loading index columns' });
// const indexcols = this.driver.__analyserInternals.skipIndexes
// ? { rows: [] }
// : await this.driver.query(this.pool, this.createQuery('indexcols', ['tables']));
this.feedback({ analysingMessage: 'Loading unique names' }); this.feedback({ analysingMessage: 'Loading unique names' });
const uniqueNames = await this.analyserQuery('uniqueNames', ['tables']); const uniqueNames = await this.analyserQuery('uniqueNames', ['tables'], { $owner: this.pool._schema_name });
this.feedback({ analysingMessage: 'Finalizing DB structure' }); this.feedback({ analysingMessage: 'Finalizing DB structure' });
const fkColumnsMapped = fkColumns.rows.map(x => ({ const fkColumnsMapped = fkColumns.rows.map(x => ({
pureName: x.pure_name, pureName: x.pure_name,
schemaName: x.schema_name, // schemaName: x.schema_name,
constraintSchema: x.constraint_schema, constraintSchema: x.constraint_schema,
constraintName: x.constraint_name, constraintName: x.constraint_name,
columnName: x.column_name, columnName: x.column_name,
@@ -114,11 +86,11 @@ class Analyser extends DatabaseAnalyser {
updateAction: x.update_action, updateAction: x.update_action,
deleteAction: x.delete_action, deleteAction: x.delete_action,
refTableName: x.ref_table_name, refTableName: x.ref_table_name,
refSchemaName: x.ref_schema_name, // refSchemaName: x.ref_schema_name,
})); }));
const pkColumnsMapped = pkColumns.rows.map(x => ({ const pkColumnsMapped = pkColumns.rows.map(x => ({
pureName: x.pure_name, pureName: x.pure_name,
schemaName: x.schema_name, // schemaName: x.schema_name,
constraintSchema: x.constraint_schema, constraintSchema: x.constraint_schema,
constraintName: x.constraint_name, constraintName: x.constraint_name,
columnName: x.column_name, columnName: x.column_name,
@@ -131,21 +103,20 @@ class Analyser extends DatabaseAnalyser {
tables: tables.rows.map(table => { tables: tables.rows.map(table => {
const newTable = { const newTable = {
pureName: table.pure_name, pureName: table.pure_name,
schemaName: table.schema_name, // schemaName: table.schema_name,
objectId: `tables:${table.schema_name}.${table.pure_name}`, objectId: `tables:${table.pure_name}`,
contentHash: table.hash_code_columns ? `${table.hash_code_columns}-${table.hash_code_constraints}` : null, contentHash: table.hash_code_columns ? `${table.hash_code_columns}-${table.hash_code_constraints}` : null,
}; };
return { return {
...newTable, ...newTable,
columns: (columnsGrouped[columnGroup(table)] || []).map(col => columns: (columnsGrouped[columnGroup(table)] || []).map(col => getColumnInfo(col, newTable)),
getColumnInfo(col, newTable, geometryColumns, geographyColumns)
),
primaryKey: DatabaseAnalyser.extractPrimaryKeys(newTable, pkColumnsMapped), primaryKey: DatabaseAnalyser.extractPrimaryKeys(newTable, pkColumnsMapped),
foreignKeys: DatabaseAnalyser.extractForeignKeys(newTable, fkColumnsMapped), foreignKeys: DatabaseAnalyser.extractForeignKeys(newTable, fkColumnsMapped),
indexes: _.uniqBy( indexes: _.uniqBy(
indexes.rows.filter( indexes.rows.filter(
idx => idx =>
idx.tableName == table.pureName && !uniqueNames.rows.find(x => x.constraintName == idx.constraintName) idx.tableName == newTable.pureName &&
!uniqueNames.rows.find(x => x.constraintName == idx.constraintName)
), ),
'constraintName' 'constraintName'
).map(idx => ({ ).map(idx => ({
@@ -155,12 +126,13 @@ class Analyser extends DatabaseAnalyser {
.filter(col => col.tableName == idx.tableName && col.constraintName == idx.constraintName) .filter(col => col.tableName == idx.tableName && col.constraintName == idx.constraintName)
.map(col => ({ .map(col => ({
..._.pick(col, ['columnName']), ..._.pick(col, ['columnName']),
isDescending: col.descending == 'DESC',
})), })),
})), })),
uniques: _.uniqBy( uniques: _.uniqBy(
indexes.rows.filter( indexes.rows.filter(
idx => idx =>
idx.tableName == table.pureName && uniqueNames.rows.find(x => x.constraintName == idx.constraintName) idx.tableName == newTable.pureName && uniqueNames.rows.find(x => x.constraintName == idx.constraintName)
), ),
'constraintName' 'constraintName'
).map(idx => ({ ).map(idx => ({
@@ -174,108 +146,47 @@ class Analyser extends DatabaseAnalyser {
}; };
}), }),
views: views.rows.map(view => ({ views: views.rows.map(view => ({
objectId: `views:${view.schema_name}.${view.pure_name}`, objectId: `views:${view.pure_name}`,
pureName: view.pure_name, pureName: view.pure_name,
schemaName: view.schema_name, // schemaName: view.schema_name,
contentHash: view.hash_code, contentHash: view.hash_code,
createSql: `CREATE VIEW "${view.schema_name}"."${view.pure_name}"\nAS\n${view.create_sql}`, createSql: `CREATE VIEW "${view.pure_name}"\nAS\n${view.create_sql}`,
columns: (columnsGrouped[columnGroup(view)] || []).map(col => getColumnInfo(col)), columns: (columnsGrouped[columnGroup(view)] || []).map(col => getColumnInfo(col)),
})), })),
matviews: matviews matviews: matviews
? matviews.rows.map(matview => ({ ? matviews.rows.map(matview => ({
objectId: `matviews:${matview.schema_name}.${matview.pure_name}`, objectId: `matviews:${matview.pure_name}`,
pureName: matview.pure_name, pureName: matview.pure_name,
schemaName: matview.schema_name, // schemaName: matview.schema_name,
contentHash: matview.hash_code, contentHash: matview.hash_code,
createSql: `CREATE MATERIALIZED VIEW "${matview.schema_name}"."${matview.pure_name}"\nAS\n${matview.definition}`, createSql: `CREATE MATERIALIZED VIEW "${matview.pure_name}"\nAS\n${matview.definition}`,
columns: matviewColumns.rows columns: (columnsGrouped[columnGroup(view)] || []).map(col => getColumnInfo(col)),
.filter(col => col.pure_name == matview.pure_name && col.schema_name == matview.schema_name)
.map(col => getColumnInfo(col)),
})) }))
: undefined, : undefined,
procedures: routines.rows procedures: routines.rows
.filter(x => x.object_type == 'PROCEDURE') .filter(x => x.object_type == 'PROCEDURE')
.map(proc => ({ .map(proc => ({
objectId: `procedures:${proc.schema_name}.${proc.pure_name}`, objectId: `procedures:${proc.pure_name}`,
pureName: proc.pure_name, pureName: proc.pure_name,
schemaName: proc.schema_name, // schemaName: proc.schema_name,
createSql: `CREATE PROCEDURE "${proc.schema_name}"."${proc.pure_name}"() LANGUAGE ${proc.language}\nAS\n$$\n${proc.definition}\n$$`, createSql: `CREATE PROCEDURE "${proc.pure_name}"() LANGUAGE ${proc.language}\nAS\n$$\n${proc.definition}\n$$`,
contentHash: proc.hash_code, contentHash: proc.hash_code,
})), })),
functions: routines.rows functions: routines.rows
.filter(x => x.object_type == 'FUNCTION') .filter(x => x.object_type == 'FUNCTION')
.map(func => ({ .map(func => ({
objectId: `functions:${func.schema_name}.${func.pure_name}`, objectId: `functions:${func.pure_name}`,
createSql: `CREATE FUNCTION "${func.schema_name}"."${func.pure_name}"() RETURNS ${func.data_type} LANGUAGE ${func.language}\nAS\n$$\n${func.definition}\n$$`, createSql: `CREATE FUNCTION "${func.pure_name}"() RETURNS ${func.data_type} LANGUAGE ${func.language}\nAS\n$$\n${func.definition}\n$$`,
pureName: func.pure_name, pureName: func.pure_name,
schemaName: func.schema_name, // schemaName: func.schema_name,
contentHash: func.hash_code, contentHash: func.hash_code,
})), })),
}; };
// this.feedback({ analysingMessage: 'Debug sleep' });
// await new Promise(resolve => setTimeout(resolve, 90 * 1000));
this.feedback({ analysingMessage: null }); this.feedback({ analysingMessage: null });
return res; return res;
} }
async _getFastSnapshot() {
return null;
const tableModificationsQueryData = this.driver.dialect.stringAgg
? await this.analyserQuery('tableModifications')
: null;
const viewModificationsQueryData = await this.analyserQuery('viewModifications');
const matviewModificationsQueryData = this.driver.dialect.materializedViews
? await this.analyserQuery('matviewModifications')
: null;
const routineModificationsQueryData = await this.analyserQuery('routineModifications');
return {
tables: tableModificationsQueryData
? tableModificationsQueryData.rows.map(x => ({
objectId: `tables:${x.schema_name}.${x.pure_name}`,
pureName: x.pure_name,
schemaName: x.schema_name,
contentHash: `${x.hash_code_columns}-${x.hash_code_constraints}`,
}))
: null,
views: viewModificationsQueryData
? viewModificationsQueryData.rows.map(x => ({
objectId: `views:${x.schema_name}.${x.pure_name}`,
pureName: x.pure_name,
schemaName: x.schema_name,
contentHash: x.hash_code,
}))
: undefined,
matviews: matviewModificationsQueryData
? matviewModificationsQueryData.rows.map(x => ({
objectId: `matviews:${x.schema_name}.${x.pure_name}`,
pureName: x.pure_name,
schemaName: x.schema_name,
contentHash: x.hash_code,
}))
: undefined,
procedures: routineModificationsQueryData.rows
.filter(x => x.object_type == 'PROCEDURE')
.map(x => ({
objectId: `procedures:${x.schema_name}.${x.pure_name}`,
pureName: x.pure_name,
schemaName: x.schema_name,
contentHash: x.hash_code,
})),
functions: routineModificationsQueryData.rows
.filter(x => x.object_type == 'FUNCTION')
.map(x => ({
objectId: `functions:${x.schema_name}.${x.pure_name}`,
pureName: x.pure_name,
schemaName: x.schema_name,
contentHash: x.hash_code,
})),
};
}
} }
module.exports = Analyser; module.exports = Analyser;

View File

@@ -4,20 +4,9 @@ const stream = require('stream');
const driverBases = require('../frontend/drivers'); const driverBases = require('../frontend/drivers');
const Analyser = require('./Analyser'); const Analyser = require('./Analyser');
//--const pg = require('pg'); //--const pg = require('pg');
//const oracledb = require('oracledb'); const oracledb = require('oracledb');
const { createBulkInsertStreamBase, makeUniqueColumnNames } = require('dbgate-tools'); const { createBulkInsertStreamBase, makeUniqueColumnNames } = require('dbgate-tools');
let requireOracledb; // native module
let oracledbValue;
function getOracledb() {
if (!oracledbValue) {
oracledbValue = requireOracledb();
}
return oracledbValue;
}
/* /*
pg.types.setTypeParser(1082, 'text', val => val); // date pg.types.setTypeParser(1082, 'text', val => val); // date
pg.types.setTypeParser(1114, 'text', val => val); // timestamp without timezone pg.types.setTypeParser(1114, 'text', val => val); // timestamp without timezone
@@ -57,23 +46,31 @@ const drivers = driverBases.map(driverBase => ({
database, database,
databaseUrl, databaseUrl,
useDatabaseUrl, useDatabaseUrl,
serviceName,
ssl, ssl,
isReadOnly, isReadOnly,
authType, authType,
socketPath, socketPath,
}) { }) {
client = await getOracledb().getConnection({ client = await oracledb.getConnection({
user, user,
password, password,
connectString: useDatabaseUrl ? databaseUrl : port ? `${server}:${port}` : server, connectString: useDatabaseUrl ? databaseUrl : port ? `${server}:${port}/${serviceName}` : server,
}); });
if (database) {
await client.execute(`ALTER SESSION SET CURRENT_SCHEMA = ${database}`);
}
client._schema_name = database;
return client; return client;
}, },
async close(pool) { async close(pool) {
return pool.end(); return pool.end();
}, },
async query(client, sql) { async query(client, sql) {
//console.log('query sql', sql); if (sql.trim() == 'COMMIT;') {
sql = 'COMMIT';
}
if (sql == null) { if (sql == null) {
return { return {
rows: [], rows: [],
@@ -101,78 +98,108 @@ const drivers = driverBases.map(driverBase => ({
}); });
*/ */
// console.log('queryStream', sql); // console.log('queryStream', sql);
const query = client.queryStream(sql);
// const consumeStream = new Promise((resolve, reject) => {
let rowcount = 0;
let wasHeader = false;
query.on('metadata', row => { if (sql.trim().toLowerCase().startsWith('select')) {
// console.log('metadata', row); const query = client.queryStream(sql);
if (!wasHeader) { // const consumeStream = new Promise((resolve, reject) => {
columns = extractOracleColumns(row); let rowcount = 0;
if (columns && columns.length > 0) { let wasHeader = false;
options.recordset(columns);
query.on('metadata', row => {
// console.log('metadata', row);
if (!wasHeader) {
columns = extractOracleColumns(row);
if (columns && columns.length > 0) {
options.recordset(columns);
}
wasHeader = true;
} }
wasHeader = true;
}
// options.row(zipDataRow(row, columns)); // options.row(zipDataRow(row, columns));
});
query.on('data', row => {
// console.log('stream DATA');
if (!wasHeader) {
columns = extractOracleColumns(row);
if (columns && columns.length > 0) {
options.recordset(columns);
}
wasHeader = true;
}
options.row(zipDataRow(row, columns));
});
query.on('end', () => {
const { command, rowCount } = query._result || {};
if (command != 'SELECT' && _.isNumber(rowCount)) {
options.info({
message: `${rowCount} rows affected`,
time: new Date(),
severity: 'info',
});
}
if (!wasHeader) {
columns = extractOracleColumns(query._result);
if (columns && columns.length > 0) {
options.recordset(columns);
}
wasHeader = true;
}
options.done();
});
query.on('error', error => {
console.log('ERROR', error);
const { message, lineNumber, procName } = error;
options.info({
message,
line: lineNumber,
procedure: procName,
time: new Date(),
severity: 'error',
}); });
options.done();
});
query.on('close', function () {
//console.log("stream 'close' event");
// The underlying ResultSet has been closed, so the connection can now
// be closed, if desired. Note: do not close connections on 'end'.
//resolve(rowcount);
});
//});
query.on('data', row => {
// console.log('stream DATA');
if (!wasHeader) {
columns = extractOracleColumns(row);
if (columns && columns.length > 0) {
options.recordset(columns);
}
wasHeader = true;
}
options.row(zipDataRow(row, columns));
});
query.on('end', () => {
const { command, rowCount } = query._result || {};
if (command != 'SELECT' && _.isNumber(rowCount)) {
options.info({
message: `${rowCount} rows affected`,
time: new Date(),
severity: 'info',
});
}
if (!wasHeader) {
columns = extractOracleColumns(query._result);
if (columns && columns.length > 0) {
options.recordset(columns);
}
wasHeader = true;
}
options.done();
});
query.on('error', error => {
console.log('ERROR', error);
const { message, lineNumber, procName } = error;
options.info({
message,
line: lineNumber,
procedure: procName,
time: new Date(),
severity: 'error',
});
options.done();
});
query.on('close', function () {
//console.log("stream 'close' event");
// The underlying ResultSet has been closed, so the connection can now
// be closed, if desired. Note: do not close connections on 'end'.
//resolve(rowcount);
});
//});
} else {
client.execute(sql, (err, res) => {
if (err) {
console.log('Error query', err, sql);
options.info({
message: err.message,
time: new Date(),
severity: 'error',
});
} else {
const { rowsAffected, metaData, rows } = res || {};
if (rows && metaData) {
const columns = extractOracleColumns(metaData);
options.recordset(columns);
for (const row of rows) {
options.row(zipDataRow(row, columns));
}
} else if (rowsAffected) {
options.info({
message: `${rowsAffected} rows affected`,
time: new Date(),
severity: 'info',
});
}
}
options.done();
});
}
//const numrows = await consumeStream; //const numrows = await consumeStream;
//console.log('Rows selected: ' + numrows); //console.log('Rows selected: ' + numrows);
//client.query(query); //client.query(query);
@@ -262,10 +289,10 @@ const drivers = driverBases.map(driverBase => ({
}, },
async writeTable(pool, name, options) { async writeTable(pool, name, options) {
// @ts-ignore // @ts-ignore
return createBulkInsertStreamBase(this, stream, pool, name, options); return createBulkInsertStreamBase(this, stream, pool, name, { ...options, commitAfterInsert: true });
}, },
async listDatabases(client) { async listDatabases(client) {
const { rows } = await this.query(client, 'SELECT instance_name AS "name" FROM v$instance'); const { rows } = await this.query(client, 'SELECT username as "name" from all_users order by username');
return rows; return rows;
}, },
@@ -283,10 +310,4 @@ const drivers = driverBases.map(driverBase => ({
}, },
})); }));
drivers.initialize = dbgateEnv => {
if (dbgateEnv.nativeModules && dbgateEnv.nativeModules.oracledb) {
requireOracledb = dbgateEnv.nativeModules.oracledb;
}
};
module.exports = drivers; module.exports = drivers;

View File

@@ -3,7 +3,4 @@ const drivers = require('./drivers');
module.exports = { module.exports = {
packageName: 'dbgate-plugin-oracle', packageName: 'dbgate-plugin-oracle',
drivers, drivers,
initialize(dbgateEnv) {
drivers.initialize(dbgateEnv);
},
}; };

View File

@@ -1,6 +1,6 @@
module.exports = ` module.exports = `
select select
owner as "schema_name", -- owner as "schema_name",
table_name as "pure_name", table_name as "pure_name",
column_name as "column_name", column_name as "column_name",
nullable as "is_nullable", nullable as "is_nullable",
@@ -10,6 +10,6 @@ select
data_scale as "numeric_scale", data_scale as "numeric_scale",
data_default as "default_value" data_default as "default_value"
FROM all_tab_columns av FROM all_tab_columns av
where TABLE_NAME =OBJECT_ID_CONDITION where OWNER='$owner' AND 'tables:' || TABLE_NAME =OBJECT_ID_CONDITION
order by column_id order by column_id
`; `;

View File

@@ -1,2 +0,0 @@
module.exports = `
`;

View File

@@ -1,2 +0,0 @@
module.exports = `
`;

View File

@@ -1,2 +0,0 @@
module.exports = `
`;

View File

@@ -1,16 +1,16 @@
module.exports = ` module.exports = `
select fk.constraint_name as "constraint_name", select fk.constraint_name as "constraint_name",
fk.owner as "constraint_schema", -- fk.owner as "constraint_schema",
fk.table_name as "pure_name", fk.table_name as "pure_name",
fk.owner as "schema_name", -- fk.owner as "schema_name",
fk.delete_rule as "update_action", fk.delete_rule as "update_action",
fk.delete_rule as "delete_action", fk.delete_rule as "delete_action",
ref.table_name as "ref_table_name", ref.table_name as "ref_table_name",
ref.owner as "ref_schema_name", -- ref.owner as "ref_schema_name",
basecol.column_name as "column_name", basecol.column_name as "column_name",
refcol.column_name as "ref_column_name" refcol.column_name as "ref_column_name"
from all_cons_columns refcol, all_cons_columns basecol, all_constraints ref, all_constraints fk from all_cons_columns refcol, all_cons_columns basecol, all_constraints ref, all_constraints fk
where fk.constraint_type = 'R' where fk.OWNER = '$owner' AND fk.constraint_type = 'R'
and ref.owner = fk.r_owner and ref.owner = fk.r_owner
and ref.constraint_name = fk.r_constraint_name and ref.constraint_name = fk.r_constraint_name
and basecol.owner = fk.owner and basecol.owner = fk.owner
@@ -19,6 +19,6 @@ and basecol.table_name = fk.table_name
and refcol.owner = ref.owner and refcol.owner = ref.owner
and refcol.constraint_name = ref.constraint_name and refcol.constraint_name = ref.constraint_name
and refcol.table_name = ref.table_name and refcol.table_name = ref.table_name
AND fk.constraint_name =OBJECT_ID_CONDITION AND 'tables:' || fk.table_name =OBJECT_ID_CONDITION
order by basecol.position order by basecol.position
`; `;

View File

@@ -1,2 +0,0 @@
module.exports = `
`;

View File

@@ -1,2 +0,0 @@
module.exports = `
`;

View File

@@ -1,15 +1,10 @@
const columns = require('./columns'); const columns = require('./columns');
const tableModifications = require('./tableList');
const tableList = require('./tableList'); const tableList = require('./tableList');
const viewModifications = require('./views');
const matviewModifications = require('./matviews');
const primaryKeys = require('./primaryKeys'); const primaryKeys = require('./primaryKeys');
const foreignKeys = require('./foreignKeys'); const foreignKeys = require('./foreignKeys');
const views = require('./views'); const views = require('./views');
const matviews = require('./matviews'); const matviews = require('./matviews');
const routines = require('./routines'); const routines = require('./routines');
const routineModifications = require('./routines');
const matviewColumns = require('./matviewColumns');
const indexes = require('./indexes'); // use mysql const indexes = require('./indexes'); // use mysql
//const indexcols = require('./indexcols'); //const indexcols = require('./indexcols');
const uniqueNames = require('./uniqueNames'); const uniqueNames = require('./uniqueNames');
@@ -22,17 +17,12 @@ const uniqueNames = require('./uniqueNames');
module.exports = { module.exports = {
columns, columns,
tableModifications,
tableList, tableList,
viewModifications,
primaryKeys, primaryKeys,
foreignKeys, foreignKeys,
views, views,
routines, routines,
routineModifications,
matviews, matviews,
matviewModifications,
matviewColumns,
indexes, indexes,
// indexcols, // indexcols,
uniqueNames, uniqueNames,

View File

@@ -1,2 +0,0 @@
module.exports = `
`;

View File

@@ -1,16 +1,15 @@
module.exports = ` module.exports = `
select i.table_name as "tableName", select i.table_name as "tableName",
i.table_owner as "schemaName", -- i.table_owner as "schemaName",
i.index_name as "constraintName", i.index_name as "constraintName",
i.index_type as "indexType", i.index_type as "indexType",
i.uniqueness as "Unique", i.uniqueness as "Unique",
ic.column_name as "columnName", ic.column_name as "columnName",
ic.column_position as "postion",
ic.descend as "descending" ic.descend as "descending"
from all_ind_columns ic, all_indexes i from all_ind_columns ic, all_indexes i
where ic.index_owner = i.owner where INDEX_OWNER = '$owner' AND ic.index_owner = i.owner
and ic.index_name = i.index_name and ic.index_name = i.index_name
and i.index_name =OBJECT_ID_CONDITION and 'tables:' || i.table_name =OBJECT_ID_CONDITION
order by i.table_owner, order by i.table_owner,
i.table_name, i.table_name,
i.index_name, i.index_name,

View File

@@ -1,9 +0,0 @@
module.exports = `
SELECT owner "schema_name"
, table_name "pure_name"
, column_name "column_name"
, data_type "data_type"
FROM all_tab_columns av
where table_name =OBJECT_ID_CONDITION
order by column_id
`;

View File

@@ -1,2 +0,0 @@
module.exports = `
`;

View File

@@ -1,5 +1,5 @@
module.exports = ` module.exports = `
SELECT owner as schema_name, SELECT -- owner as schema_name,
mview_name pure_name, mview_name pure_name,
container_name, container_name,
'' || trim( '' || trim(
@@ -14,6 +14,6 @@ SELECT owner as schema_name,
'//text()' '//text()'
)) definition )) definition
FROM all_mviews FROM all_mviews
where mview_name=OBJECT_ID_CONDITION where OWNER = '$owner' AND 'matviews:' || mview_name=OBJECT_ID_CONDITION
order by owner, mview_name order by owner, mview_name
`; `;

View File

@@ -1,8 +1,8 @@
module.exports = ` module.exports = `
select select
pk.owner as "constraint_schema", -- pk.owner as "constraint_schema",
pk.constraint_name as "constraint_name", pk.constraint_name as "constraint_name",
pk.owner as "schema_name", -- pk.owner as "schema_name",
pk.table_name as "pure_name", pk.table_name as "pure_name",
basecol.column_name as "column_name" basecol.column_name as "column_name"
from all_cons_columns basecol, from all_cons_columns basecol,
@@ -11,6 +11,7 @@ where constraint_type = 'P'
and basecol.owner = pk.owner and basecol.owner = pk.owner
and basecol.constraint_name = pk.constraint_name and basecol.constraint_name = pk.constraint_name
and basecol.table_name = pk.table_name and basecol.table_name = pk.table_name
and pk.constraint_name =OBJECT_ID_CONDITION and 'tables:' || basecol.table_name =OBJECT_ID_CONDITION
and pk.owner = '$owner'
order by basecol.position order by basecol.position
`; `;

View File

@@ -1,2 +0,0 @@
module.exports = `
`;

View File

@@ -1,7 +1,7 @@
module.exports = ` module.exports = `
select select
routine_name as "pure_name", routine_name as "pure_name",
routine_schema as "schema_name", -- routine_schema as "schema_name",
routine_definition as "definition", routine_definition as "definition",
standard_hash(routine_definition, 'MD5') as "hash_code", standard_hash(routine_definition, 'MD5') as "hash_code",
routine_type as "object_type", routine_type as "object_type",
@@ -32,6 +32,7 @@ from (select
all_procedures ap, all_procedures ap,
all_objects ao all_objects ao
where where
ap.owner = '$owner' and
ap.owner = ao.owner and ap.owner = ao.owner and
ap.object_name = ao.object_name and ap.object_name = ao.object_name and
ao.object_type in ('PACKAGE', 'PROCEDURE', 'FUNCTION') ao.object_type in ('PACKAGE', 'PROCEDURE', 'FUNCTION')

View File

@@ -1,9 +1,9 @@
module.exports = ` module.exports = `
select select
owner "schema_name", -- owner "schema_name",
table_name "pure_name" table_name "pure_name"
from from
all_tables all_tables
where TABLE_NAME =OBJECT_ID_CONDITION where OWNER='$owner' AND 'tables:' || TABLE_NAME =OBJECT_ID_CONDITION
`; `;

View File

@@ -1,2 +0,0 @@
module.exports = `
`;

View File

@@ -1,6 +1,6 @@
module.exports = ` module.exports = `
select constraint_name select constraint_name as "constraintName"
from all_constraints from all_constraints
where constraint_type = 'U' where owner='$owner' and constraint_type = 'U'
and constraint_name =OBJECT_ID_CONDITION and 'tables:' || table_name =OBJECT_ID_CONDITION
`; `;

View File

@@ -1,2 +0,0 @@
module.exports = `
`;

View File

@@ -3,10 +3,10 @@ select avv.*,
ora_hash("create_sql") as "hash_code" ora_hash("create_sql") as "hash_code"
from (select from (select
view_name as "pure_name", view_name as "pure_name",
owner as "schema_name", -- owner as "schema_name",
SUBSTR(text_vc, 1, 3900) AS "create_sql" SUBSTR(text_vc, 1, 3900) AS "create_sql"
from all_views av from all_views av
where text_vc is not null where owner = '$owner' and text_vc is not null
) avv ) avv
where "pure_name" =OBJECT_ID_CONDITION where 'views:' || "pure_name" =OBJECT_ID_CONDITION
`; `;

View File

@@ -1,100 +1,123 @@
const { SqlDumper, arrayToHexString, testEqualTypes } = global.DBGATE_TOOLS; const { SqlDumper, arrayToHexString, testEqualTypes } = global.DBGATE_TOOLS;
class Dumper extends SqlDumper { class Dumper extends SqlDumper {
/** @param type {import('dbgate-types').TransformType} */ createDatabase(name) {
transform(type, dumpExpr) { this.putCmd(
switch (type) { `CREATE USER c##${name}
case 'GROUP:YEAR': IDENTIFIED BY ${name}
case 'YEAR': DEFAULT TABLESPACE users
this.put('^extract(^year ^from %c)', dumpExpr); TEMPORARY TABLESPACE temp
break; QUOTA 10M ON users;`,
case 'MONTH': name
this.put('^extract(^month ^from %c)', dumpExpr); );
break;
case 'DAY':
this.put('^extract(^day ^from %c)', dumpExpr);
break;
case 'GROUP:MONTH':
this.put("^to_char(%c, '%s')", dumpExpr, 'YYYY-MM');
break;
case 'GROUP:DAY':
this.put("^to_char(%c, '%s')", dumpExpr, 'YYYY-MM-DD');
break;
default:
dumpExpr();
break;
}
} }
dropRecreatedTempTable(tmptable) { // oracle uses implicit transactions
this.putCmd('^drop ^table %i ^cascade', tmptable); beginTransaction() {}
}
renameTable(obj, newname) {
this.putCmd('^alter ^table %f ^rename ^to %i', obj, newname);
}
renameColumn(column, newcol) {
this.putCmd('^alter ^table %f ^rename ^column %i ^to %i', column, column.columnName, newcol);
}
dropTable(obj, options = {}) {
this.put('^drop ^table');
if (options.testIfExists) this.put(' ^if ^exists');
this.put(' %f', obj);
this.endCommand();
}
//public override void CreateIndex(IndexInfo ix)
//{
//}
enableConstraints(table, enabled) {
this.putCmd('^alter ^table %f %k ^trigger ^all', table, enabled ? 'enable' : 'disable');
}
columnDefinition(col, options) { columnDefinition(col, options) {
if (col.autoIncrement) { if (col.autoIncrement) {
this.put('^serial'); super.columnType(col.dataType);
this.put(' ^generated ^by ^default ^on ^null ^as ^identity');
return; return;
} }
super.columnDefinition(col, options); super.columnDefinition(col, options);
} }
changeColumn(oldcol, newcol, constraints) { // /** @param type {import('dbgate-types').TransformType} */
if (oldcol.columnName != newcol.columnName) { // transform(type, dumpExpr) {
this.putCmd('^alter ^table %f ^rename ^column %i ^to %i', oldcol, oldcol.columnName, newcol.columnName); // switch (type) {
} // case 'GROUP:YEAR':
if (!testEqualTypes(oldcol, newcol)) { // case 'YEAR':
this.putCmd('^alter ^table %f ^alter ^column %i ^type %s', oldcol, newcol.columnName, newcol.dataType); // this.put('^extract(^year ^from %c)', dumpExpr);
} // break;
if (oldcol.notNull != newcol.notNull) { // case 'MONTH':
if (newcol.notNull) this.putCmd('^alter ^table %f ^alter ^column %i ^set ^not ^null', newcol, newcol.columnName); // this.put('^extract(^month ^from %c)', dumpExpr);
else this.putCmd('^alter ^table %f ^alter ^column %i ^drop ^not ^null', newcol, newcol.columnName); // break;
} // case 'DAY':
if (oldcol.defaultValue != newcol.defaultValue) { // this.put('^extract(^day ^from %c)', dumpExpr);
if (newcol.defaultValue == null) { // break;
this.putCmd('^alter ^table %f ^alter ^column %i ^drop ^default', newcol, newcol.columnName); // case 'GROUP:MONTH':
} else { // this.put("^to_char(%c, '%s')", dumpExpr, 'YYYY-MM');
this.putCmd( // break;
'^alter ^table %f ^alter ^column %i ^set ^default %s', // case 'GROUP:DAY':
newcol, // this.put("^to_char(%c, '%s')", dumpExpr, 'YYYY-MM-DD');
newcol.columnName, // break;
newcol.defaultValue // default:
); // dumpExpr();
} // break;
} // }
} // }
putValue(value) { // dropRecreatedTempTable(tmptable) {
if (value === true) this.putRaw('true'); // this.putCmd('^drop ^table %i ^cascade', tmptable);
else if (value === false) this.putRaw('false'); // }
else super.putValue(value);
}
putByteArrayValue(value) { // renameTable(obj, newname) {
this.putRaw(`e'\\\\x${arrayToHexString(value)}'`); // this.putCmd('^alter ^table %f ^rename ^to %i', obj, newname);
} // }
// renameColumn(column, newcol) {
// this.putCmd('^alter ^table %f ^rename ^column %i ^to %i', column, column.columnName, newcol);
// }
// dropTable(obj, options = {}) {
// this.put('^drop ^table');
// if (options.testIfExists) this.put(' ^if ^exists');
// this.put(' %f', obj);
// this.endCommand();
// }
// //public override void CreateIndex(IndexInfo ix)
// //{
// //}
// enableConstraints(table, enabled) {
// this.putCmd('^alter ^table %f %k ^trigger ^all', table, enabled ? 'enable' : 'disable');
// }
// columnDefinition(col, options) {
// if (col.autoIncrement) {
// this.put('^serial');
// return;
// }
// super.columnDefinition(col, options);
// }
// changeColumn(oldcol, newcol, constraints) {
// if (oldcol.columnName != newcol.columnName) {
// this.putCmd('^alter ^table %f ^rename ^column %i ^to %i', oldcol, oldcol.columnName, newcol.columnName);
// }
// if (!testEqualTypes(oldcol, newcol)) {
// this.putCmd('^alter ^table %f ^alter ^column %i ^type %s', oldcol, newcol.columnName, newcol.dataType);
// }
// if (oldcol.notNull != newcol.notNull) {
// if (newcol.notNull) this.putCmd('^alter ^table %f ^alter ^column %i ^set ^not ^null', newcol, newcol.columnName);
// else this.putCmd('^alter ^table %f ^alter ^column %i ^drop ^not ^null', newcol, newcol.columnName);
// }
// if (oldcol.defaultValue != newcol.defaultValue) {
// if (newcol.defaultValue == null) {
// this.putCmd('^alter ^table %f ^alter ^column %i ^drop ^default', newcol, newcol.columnName);
// } else {
// this.putCmd(
// '^alter ^table %f ^alter ^column %i ^set ^default %s',
// newcol,
// newcol.columnName,
// newcol.defaultValue
// );
// }
// }
// }
// putValue(value) {
// if (value === true) this.putRaw('true');
// else if (value === false) this.putRaw('false');
// else super.putValue(value);
// }
// putByteArrayValue(value) {
// this.putRaw(`e'\\\\x${arrayToHexString(value)}'`);
// }
} }
module.exports = Dumper; module.exports = Dumper;

View File

@@ -6,7 +6,7 @@ const spatialTypes = ['GEOGRAPHY'];
/** @type {import('dbgate-types').SqlDialect} */ /** @type {import('dbgate-types').SqlDialect} */
const dialect = { const dialect = {
rangeSelect: false, rangeSelect: true,
limitSelect: false, limitSelect: false,
offsetFetchRangeSyntax: true, offsetFetchRangeSyntax: true,
ilike: true, ilike: true,
@@ -19,7 +19,6 @@ const dialect = {
quoteIdentifier(s) { quoteIdentifier(s) {
return '"' + s + '"'; return '"' + s + '"';
}, },
stringAgg: true,
createColumn: true, createColumn: true,
dropColumn: true, dropColumn: true,
@@ -38,49 +37,30 @@ const dialect = {
dropReferencesWhenDropTable: true, dropReferencesWhenDropTable: true,
predefinedDataTypes: [ predefinedDataTypes: [
'bigint', 'VARCHAR2',
'bigserial', 'NUMBER',
'bit', 'DATE',
'varbit', 'CLOB',
'boolean', 'BLOB',
'box', 'INTEGER',
'bytea',
'char(20)', 'BFILE',
'varchar(250)', 'BINARY_DOUBLE',
'cidr', 'BINARY_FLOAT',
'circle', 'CHAR',
'date', 'FLOAT',
'double precision', 'INTERVAL DAY',
'inet', 'INTERVAL YEAR',
'int', 'LONG',
'interval', 'LONG RAW',
'json', 'NCHAR',
'jsonb', 'NCLOB',
'line', 'NVARCHAR2',
'lseg', 'RAW',
'macaddr', 'ROWID',
'macaddr8', 'TIMESTAMP',
'money', 'UROWID',
'numeric(10,2)', // 'XMLTYPE',
'path',
'pg_lsn',
'pg_snapshot',
'point',
'polygon',
'real',
'smallint',
'smallserial',
'serial',
'text',
'time',
'timetz',
'timestamp',
'timestamptz',
'tsquery',
'tsvector',
'txid_snapshot',
'uuid',
'xml',
], ],
createColumnViewExpression(columnName, dataType, source, alias) { createColumnViewExpression(columnName, dataType, source, alias) {
@@ -110,32 +90,15 @@ const oracleDriverBase = {
getQuerySplitterOptions: () => oracleSplitterOptions, getQuerySplitterOptions: () => oracleSplitterOptions,
readOnlySessions: true, readOnlySessions: true,
databaseUrlPlaceholder: 'e.g. oracledb://user:password@localhost:1521', databaseUrlPlaceholder: 'e.g. localhost:1521/orcl',
showConnectionField: (field, values) => { showConnectionField: (field, values) => {
if (field == 'useDatabaseUrl') return true; if (field == 'useDatabaseUrl') return true;
if (values.useDatabaseUrl) { if (values.useDatabaseUrl) {
return ['databaseUrl', 'isReadOnly'].includes(field); return ['databaseUrl', 'user', 'password'].includes(field);
} }
return ['user', 'password', 'defaultDatabase', 'singleDatabase', 'isReadOnly', 'server', 'port'].includes(field); return ['user', 'password', 'server', 'port', 'serviceName'].includes(field);
},
beforeConnectionSave: connection => {
const { databaseUrl } = connection;
if (databaseUrl) {
const m = databaseUrl.match(/\/([^/]+)($|\?)/);
return {
...connection,
singleDatabase: !!m,
defaultDatabase: m ? m[1] : null,
};
}
return connection;
},
__analyserInternals: {
refTableCond: '',
}, },
getNewObjectTemplates() { getNewObjectTemplates() {
@@ -168,7 +131,7 @@ $$ LANGUAGE plpgsql;`,
const oracleDriver = { const oracleDriver = {
...oracleDriverBase, ...oracleDriverBase,
engine: 'oracle@dbgate-plugin-oracle', engine: 'oracle@dbgate-plugin-oracle',
title: 'OracleDB (Experimental)', title: 'OracleDB',
defaultPort: 1521, defaultPort: 1521,
dialect: { dialect: {
...dialect, ...dialect,
@@ -189,7 +152,7 @@ const oracleDriver = {
return dialect; return dialect;
}, },
showConnectionTab: (field) => field == 'sshTunnel', showConnectionTab: field => field == 'sshTunnel',
}; };
module.exports = [oracleDriver]; module.exports = [oracleDriver];

View File

@@ -22,7 +22,7 @@ var config = {
plugins: [ plugins: [
new webpack.IgnorePlugin({ new webpack.IgnorePlugin({
checkResource(resource) { checkResource(resource) {
const lazyImports = ['oracledb', 'uws']; const lazyImports = ['uws'];
if (!lazyImports.includes(resource)) { if (!lazyImports.includes(resource)) {
return false; return false;
} }

View File

@@ -31,7 +31,7 @@
}, },
"devDependencies": { "devDependencies": {
"dbgate-plugin-tools": "^1.0.7", "dbgate-plugin-tools": "^1.0.7",
"dbgate-query-splitter": "^4.9.3", "dbgate-query-splitter": "^4.10.1",
"dbgate-tools": "^5.0.0-alpha.1", "dbgate-tools": "^5.0.0-alpha.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"pg": "^8.11.5", "pg": "^8.11.5",

View File

@@ -70,29 +70,23 @@ class Analyser extends DatabaseAnalyser {
const tables = await this.analyserQuery(this.driver.dialect.stringAgg ? 'tableModifications' : 'tableList', [ const tables = await this.analyserQuery(this.driver.dialect.stringAgg ? 'tableModifications' : 'tableList', [
'tables', 'tables',
]); ]);
this.logger.debug({ count: tables.rows.length }, 'Tables loaded');
this.feedback({ analysingMessage: 'Loading columns' }); this.feedback({ analysingMessage: 'Loading columns' });
const columns = await this.analyserQuery('columns', ['tables', 'views']); const columns = await this.analyserQuery('columns', ['tables', 'views']);
this.logger.debug({ count: columns.rows.length }, 'Columns loaded');
this.feedback({ analysingMessage: 'Loading primary keys' }); this.feedback({ analysingMessage: 'Loading primary keys' });
const pkColumns = await this.analyserQuery('primaryKeys', ['tables']); const pkColumns = await this.analyserQuery('primaryKeys', ['tables']);
this.logger.debug({ count: pkColumns.rows.length }, 'Primary keys loaded');
let fkColumns = null; let fkColumns = null;
this.feedback({ analysingMessage: 'Loading foreign key constraints' }); this.feedback({ analysingMessage: 'Loading foreign key constraints' });
const fk_tableConstraints = await this.analyserQuery('fk_tableConstraints', ['tables']); const fk_tableConstraints = await this.analyserQuery('fk_tableConstraints', ['tables']);
this.logger.debug({ count: fk_tableConstraints.rows.length }, 'Foreign keys loaded');
this.feedback({ analysingMessage: 'Loading foreign key refs' }); this.feedback({ analysingMessage: 'Loading foreign key refs' });
const fk_referentialConstraints = await this.analyserQuery('fk_referentialConstraints', ['tables']); const fk_referentialConstraints = await this.analyserQuery('fk_referentialConstraints', ['tables']);
this.logger.debug({ count: fk_referentialConstraints.rows.length }, 'Foreign key refs loaded');
this.feedback({ analysingMessage: 'Loading foreign key columns' }); this.feedback({ analysingMessage: 'Loading foreign key columns' });
const fk_keyColumnUsage = await this.analyserQuery('fk_keyColumnUsage', ['tables']); const fk_keyColumnUsage = await this.analyserQuery('fk_keyColumnUsage', ['tables']);
this.logger.debug({ count: fk_keyColumnUsage.rows.length }, 'Foreign key columns loaded');
const cntKey = x => `${x.constraint_name}|${x.constraint_schema}`; const cntKey = x => `${x.constraint_name}|${x.constraint_schema}`;
const fkRows = []; const fkRows = [];
@@ -134,50 +128,41 @@ class Analyser extends DatabaseAnalyser {
this.feedback({ analysingMessage: 'Loading views' }); this.feedback({ analysingMessage: 'Loading views' });
const views = await this.analyserQuery('views', ['views']); const views = await this.analyserQuery('views', ['views']);
this.logger.debug({ count: views.rows.length }, 'Views loaded');
this.feedback({ analysingMessage: 'Loading materialized views' }); this.feedback({ analysingMessage: 'Loading materialized views' });
const matviews = this.driver.dialect.materializedViews ? await this.analyserQuery('matviews', ['matviews']) : null; const matviews = this.driver.dialect.materializedViews ? await this.analyserQuery('matviews', ['matviews']) : null;
this.logger.debug({ count: matviews.rows.length }, 'Materialized views loaded');
this.feedback({ analysingMessage: 'Loading materialized view columns' }); this.feedback({ analysingMessage: 'Loading materialized view columns' });
const matviewColumns = this.driver.dialect.materializedViews const matviewColumns = this.driver.dialect.materializedViews
? await this.analyserQuery('matviewColumns', ['matviews']) ? await this.analyserQuery('matviewColumns', ['matviews'])
: null; : null;
this.logger.debug({ count: matviewColumns.rows.length }, 'Materialized view columns loaded');
this.feedback({ analysingMessage: 'Loading routines' }); this.feedback({ analysingMessage: 'Loading routines' });
const routines = await this.analyserQuery('routines', ['procedures', 'functions']); const routines = await this.analyserQuery('routines', ['procedures', 'functions']);
this.logger.debug({ count: routines.rows.length }, 'Routines loaded');
this.feedback({ analysingMessage: 'Loading indexes' }); this.feedback({ analysingMessage: 'Loading indexes' });
const indexes = this.driver.__analyserInternals.skipIndexes const indexes = this.driver.__analyserInternals.skipIndexes
? { rows: [] } ? { rows: [] }
: await this.analyserQuery('indexes', ['tables']); : await this.analyserQuery('indexes', ['tables']);
this.logger.debug({ count: indexes.rows.length }, 'Indexes loaded');
this.feedback({ analysingMessage: 'Loading index columns' }); this.feedback({ analysingMessage: 'Loading index columns' });
const indexcols = this.driver.__analyserInternals.skipIndexes const indexcols = this.driver.__analyserInternals.skipIndexes
? { rows: [] } ? { rows: [] }
: await this.analyserQuery('indexcols', ['tables']); : await this.analyserQuery('indexcols', ['tables']);
this.logger.debug({ count: indexcols.rows.length }, 'Indexes columns loaded');
this.feedback({ analysingMessage: 'Loading unique names' }); this.feedback({ analysingMessage: 'Loading unique names' });
const uniqueNames = await this.analyserQuery('uniqueNames', ['tables']); const uniqueNames = await this.analyserQuery('uniqueNames', ['tables']);
this.logger.debug({ count: uniqueNames.rows.length }, 'Uniques loaded');
let geometryColumns = { rows: [] }; let geometryColumns = { rows: [] };
if (views.rows.find(x => x.pure_name == 'geometry_columns' && x.schema_name == 'public')) { if (views.rows.find(x => x.pure_name == 'geometry_columns' && x.schema_name == 'public')) {
this.feedback({ analysingMessage: 'Loading geometry columns' }); this.feedback({ analysingMessage: 'Loading geometry columns' });
geometryColumns = await this.analyserQuery('geometryColumns', ['tables']); geometryColumns = await this.analyserQuery('geometryColumns', ['tables']);
this.logger.debug({ count: geometryColumns.rows.length }, 'Geometry columns loaded');
} }
let geographyColumns = { rows: [] }; let geographyColumns = { rows: [] };
if (views.rows.find(x => x.pure_name == 'geography_columns' && x.schema_name == 'public')) { if (views.rows.find(x => x.pure_name == 'geography_columns' && x.schema_name == 'public')) {
this.feedback({ analysingMessage: 'Loading geography columns' }); this.feedback({ analysingMessage: 'Loading geography columns' });
geographyColumns = await this.analyserQuery('geographyColumns', ['tables']); geographyColumns = await this.analyserQuery('geographyColumns', ['tables']);
this.logger.debug({ count: geographyColumns.rows.length }, 'Geography columns loaded');
} }
this.feedback({ analysingMessage: 'Finalizing DB structure' }); this.feedback({ analysingMessage: 'Finalizing DB structure' });

View File

@@ -33,6 +33,7 @@ const dialect = {
dropUnique: true, dropUnique: true,
createCheck: true, createCheck: true,
dropCheck: true, dropCheck: true,
allowMultipleValuesInsert: true,
dropReferencesWhenDropTable: true, dropReferencesWhenDropTable: true,
requireStandaloneSelectForScopeIdentity: true, requireStandaloneSelectForScopeIdentity: true,

View File

@@ -30,7 +30,7 @@
}, },
"devDependencies": { "devDependencies": {
"dbgate-plugin-tools": "^1.0.7", "dbgate-plugin-tools": "^1.0.7",
"dbgate-query-splitter": "^4.9.3", "dbgate-query-splitter": "^4.10.1",
"dbgate-tools": "^5.0.0-alpha.1", "dbgate-tools": "^5.0.0-alpha.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"webpack": "^5.91.0", "webpack": "^5.91.0",

View File

@@ -32,7 +32,7 @@
"devDependencies": { "devDependencies": {
"dbgate-tools": "^5.0.0-alpha.1", "dbgate-tools": "^5.0.0-alpha.1",
"dbgate-plugin-tools": "^1.0.4", "dbgate-plugin-tools": "^1.0.4",
"dbgate-query-splitter": "^4.9.3", "dbgate-query-splitter": "^4.10.1",
"byline": "^5.0.0", "byline": "^5.0.0",
"webpack": "^5.91.0", "webpack": "^5.91.0",
"webpack-cli": "^5.1.4" "webpack-cli": "^5.1.4"

View File

@@ -17,6 +17,7 @@ const dialect = {
explicitDropConstraint: true, explicitDropConstraint: true,
stringEscapeChar: "'", stringEscapeChar: "'",
fallbackDataType: 'nvarchar', fallbackDataType: 'nvarchar',
allowMultipleValuesInsert: true,
dropColumnDependencies: ['indexes', 'primaryKey', 'uniques'], dropColumnDependencies: ['indexes', 'primaryKey', 'uniques'],
quoteIdentifier(s) { quoteIdentifier(s) {
return `[${s}]`; return `[${s}]`;

View File

@@ -3202,10 +3202,10 @@ dbgate-plugin-xml@^5.0.0-alpha.1:
resolved "https://registry.yarnpkg.com/dbgate-plugin-xml/-/dbgate-plugin-xml-5.2.7.tgz#0762af51ba6f100e75a63907ea6c679e827c9f7c" resolved "https://registry.yarnpkg.com/dbgate-plugin-xml/-/dbgate-plugin-xml-5.2.7.tgz#0762af51ba6f100e75a63907ea6c679e827c9f7c"
integrity sha512-gBXy4qetf7eJQW6lM01B+OKLnKB8MKesojdYKysD9oZ+YpQCX8Tq7aHJCrN14FiyIDinpX61kmFH1+LGJ2RkxQ== integrity sha512-gBXy4qetf7eJQW6lM01B+OKLnKB8MKesojdYKysD9oZ+YpQCX8Tq7aHJCrN14FiyIDinpX61kmFH1+LGJ2RkxQ==
dbgate-query-splitter@^4.9.0, dbgate-query-splitter@^4.9.3: dbgate-query-splitter@^4.10.1:
version "4.9.3" version "4.10.1"
resolved "https://registry.yarnpkg.com/dbgate-query-splitter/-/dbgate-query-splitter-4.9.3.tgz#f66396da9ae3cc8f775a282143bfca3441248aa2" resolved "https://registry.yarnpkg.com/dbgate-query-splitter/-/dbgate-query-splitter-4.10.1.tgz#dc40d792de06f779a743cad054d5e786006b03a9"
integrity sha512-QMppAy3S6NGQMawNokmhbpZURvLCETyu/8yTfqWUHGdlK963fdSpmoX1A+9SjCDp62sX0vYntfD7uzd6jVSRcw== integrity sha512-KqrB7NLP1jXbx8rN7gSmYUVorm6ICeqOV+oR+jHaBLXqqhWepHsKr6JJlFEeb/LhoVjnTDY/cy5zhW1dMIQF6A==
debug@2.6.9, debug@^2.2.0, debug@^2.3.3: debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
version "2.6.9" version "2.6.9"
@@ -7978,10 +7978,10 @@ optionator@^0.8.1, optionator@^0.8.3:
resolved "https://registry.yarnpkg.com/opts/-/opts-2.0.2.tgz#a17e189fbbfee171da559edd8a42423bc5993ce1" resolved "https://registry.yarnpkg.com/opts/-/opts-2.0.2.tgz#a17e189fbbfee171da559edd8a42423bc5993ce1"
integrity sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg== integrity sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==
oracledb@^5.5.0: oracledb@^6.5.1:
version "5.5.0" version "6.5.1"
resolved "https://registry.yarnpkg.com/oracledb/-/oracledb-5.5.0.tgz#0cf9af5d0c0815f74849ae9ed56aee823514d71b" resolved "https://registry.yarnpkg.com/oracledb/-/oracledb-6.5.1.tgz#814d985035acdb1a6470b1152af0ca3767569ede"
integrity sha512-i5cPvMENpZP8nnqptB6l0pjiOyySj1IISkbM4Hr3yZEDdANo2eezarwZb9NQ8fTh5pRjmgpZdSyIbnn9N3AENw== integrity sha512-JzoSGei1wnvmqgKnAZK1W650mzHTZXx+7hClV4mwsbY/ZjUtrpnojNJMYJ2jkOhj7XG5oJPfXc4GqDKaNzkxqg==
os-tmpdir@~1.0.2: os-tmpdir@~1.0.2:
version "1.0.2" version "1.0.2"