diff --git a/packages/api/src/controllers/connections.js b/packages/api/src/controllers/connections.js index c17566cb3..2cda2e876 100644 --- a/packages/api/src/controllers/connections.js +++ b/packages/api/src/controllers/connections.js @@ -61,6 +61,7 @@ function getPortalCollections() { useDatabaseUrl: !!process.env[`URL_${id}`], databaseFile: process.env[`FILE_${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), defaultDatabase: process.env[`DATABASE_${id}`] || diff --git a/packages/tools/src/DatabaseAnalyser.ts b/packages/tools/src/DatabaseAnalyser.ts index 699ce6d65..0480d0aff 100644 --- a/packages/tools/src/DatabaseAnalyser.ts +++ b/packages/tools/src/DatabaseAnalyser.ts @@ -180,8 +180,15 @@ export class DatabaseAnalyser { // return this.createQueryCore('=OBJECT_ID_CONDITION', typeFields) != ' is not null'; // } - createQuery(template, typeFields) { - return this.createQueryCore(template, typeFields); + createQuery(template, typeFields, replacements = {}) { + 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) { @@ -302,8 +309,8 @@ export class DatabaseAnalyser { return [..._compact(res), ...this.getDeletedObjects(snapshot)]; } - async analyserQuery(template, typeFields) { - const sql = this.createQuery(template, typeFields); + async analyserQuery(template, typeFields, replacements = {}) { + const sql = this.createQuery(template, typeFields, replacements); if (!sql) { return { @@ -311,7 +318,9 @@ export class DatabaseAnalyser { }; } 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) { logger.error({ err }, 'Error running analyser query'); return { diff --git a/packages/web/src/settings/ConnectionDriverFields.svelte b/packages/web/src/settings/ConnectionDriverFields.svelte index eb3e1a6ab..0bfdc127d 100644 --- a/packages/web/src/settings/ConnectionDriverFields.svelte +++ b/packages/web/src/settings/ConnectionDriverFields.svelte @@ -123,6 +123,10 @@ {/if} {/if} +{#if driver?.showConnectionField('serviceName', $values)} + +{/if} + {#if driver?.showConnectionField('socketPath', $values)} driver?.showConnectionField(x, $values)); const omitProps = _.difference(allProps, visibleProps); diff --git a/packages/web/src/utility/getConnectionLabel.ts b/packages/web/src/utility/getConnectionLabel.ts index 034e975f6..2350ecc84 100644 --- a/packages/web/src/utility/getConnectionLabel.ts +++ b/packages/web/src/utility/getConnectionLabel.ts @@ -24,6 +24,9 @@ function getConnectionLabelCore(connection, { allowExplicitDatabase = true } = { if (connection.singleDatabase && connection.defaultDatabase) { return `${connection.defaultDatabase}`; } + if (connection.useDatabaseUrl) { + return `${connection.databaseUrl}`; + } return ''; } diff --git a/plugins/dbgate-plugin-oracle/src/backend/Analyser.js b/plugins/dbgate-plugin-oracle/src/backend/Analyser.js index e5e39e8f5..e95dbb6ad 100644 --- a/plugins/dbgate-plugin-oracle/src/backend/Analyser.js +++ b/plugins/dbgate-plugin-oracle/src/backend/Analyser.js @@ -55,8 +55,8 @@ class Analyser extends DatabaseAnalyser { super(pool, driver, version); } - createQuery(resFileName, typeFields) { - const query = super.createQuery(sql[resFileName], typeFields); + createQuery(resFileName, typeFields, replacements = {}) { + const query = super.createQuery(sql[resFileName], typeFields, replacements); //if (query) return query.replace('#REFTABLECOND#', this.driver.__analyserInternals.refTableCond); return query; } @@ -68,40 +68,39 @@ class Analyser extends DatabaseAnalyser { async _runAnalysis() { 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' }); - 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' }); - const pkColumns = await this.analyserQuery('primaryKeys', ['tables']); + const pkColumns = await this.analyserQuery('primaryKeys', ['tables'], { $owner: this.pool._schema_name }); //let fkColumns = null; 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' }); - 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' }); - const matviews = this.driver.dialect.materializedViews ? await this.analyserQuery('matviews', ['matviews']) : null; + const matviews = this.driver.dialect.materializedViews + ? await this.analyserQuery('matviews', ['matviews'], { $owner: this.pool._schema_name }) + : null; this.feedback({ analysingMessage: 'Loading materialized view columns' }); const matviewColumns = this.driver.dialect.materializedViews - ? await this.analyserQuery('matviewColumns', ['matviews']) + ? await this.analyserQuery('matviewColumns', ['matviews'], { $owner: this.pool._schema_name }) : null; 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' }); - const indexes = this.driver.__analyserInternals.skipIndexes - ? { rows: [] } - : await this.analyserQuery('indexes', ['tables']); + const indexes = await this.analyserQuery('indexes', ['tables'], { $owner: this.pool._schema_name }); 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' }); - 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' }); const fkColumnsMapped = fkColumns.rows.map(x => ({ diff --git a/plugins/dbgate-plugin-oracle/src/backend/drivers.js b/plugins/dbgate-plugin-oracle/src/backend/drivers.js index 388dc1772..57ae6d947 100644 --- a/plugins/dbgate-plugin-oracle/src/backend/drivers.js +++ b/plugins/dbgate-plugin-oracle/src/backend/drivers.js @@ -7,7 +7,6 @@ const Analyser = require('./Analyser'); const oracledb = require('oracledb'); const { createBulkInsertStreamBase, makeUniqueColumnNames } = require('dbgate-tools'); - /* pg.types.setTypeParser(1082, 'text', val => val); // date pg.types.setTypeParser(1114, 'text', val => val); // timestamp without timezone @@ -47,6 +46,7 @@ const drivers = driverBases.map(driverBase => ({ database, databaseUrl, useDatabaseUrl, + serviceName, ssl, isReadOnly, authType, @@ -55,8 +55,9 @@ const drivers = driverBases.map(driverBase => ({ client = await oracledb.getConnection({ user, password, - connectString: useDatabaseUrl ? databaseUrl : port ? `${server}:${port}` : server, + connectString: useDatabaseUrl ? databaseUrl : port ? `${server}:${port}/${serviceName}` : server, }); + client._schema_name = database; return client; }, async close(pool) { @@ -64,7 +65,7 @@ const drivers = driverBases.map(driverBase => ({ }, async query(client, sql) { //console.log('query sql', sql); - if (sql == null) { + if (sql == null) {a return { rows: [], columns: [], @@ -250,12 +251,12 @@ const drivers = driverBases.map(driverBase => ({ return pass; }, - async writeTable(pool, name, options) { + async writeTable(pootl, name, options) { // @ts-ignore return createBulkInsertStreamBase(this, stream, pool, name, options); }, 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; }, diff --git a/plugins/dbgate-plugin-oracle/src/backend/sql/columns.js b/plugins/dbgate-plugin-oracle/src/backend/sql/columns.js index 3e046a39a..09b086d7a 100644 --- a/plugins/dbgate-plugin-oracle/src/backend/sql/columns.js +++ b/plugins/dbgate-plugin-oracle/src/backend/sql/columns.js @@ -10,6 +10,6 @@ select data_scale as "numeric_scale", data_default as "default_value" FROM all_tab_columns av - where TABLE_NAME =OBJECT_ID_CONDITION + where OWNER='$owner' AND TABLE_NAME =OBJECT_ID_CONDITION order by column_id `; \ No newline at end of file diff --git a/plugins/dbgate-plugin-oracle/src/backend/sql/foreignKeys.js b/plugins/dbgate-plugin-oracle/src/backend/sql/foreignKeys.js index ffaa26f50..4a8b3a891 100644 --- a/plugins/dbgate-plugin-oracle/src/backend/sql/foreignKeys.js +++ b/plugins/dbgate-plugin-oracle/src/backend/sql/foreignKeys.js @@ -10,7 +10,7 @@ select fk.constraint_name as "constraint_name", basecol.column_name as "column_name", refcol.column_name as "ref_column_name" 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.constraint_name = fk.r_constraint_name and basecol.owner = fk.owner diff --git a/plugins/dbgate-plugin-oracle/src/backend/sql/indexes.js b/plugins/dbgate-plugin-oracle/src/backend/sql/indexes.js index 32aa10437..2a2946069 100644 --- a/plugins/dbgate-plugin-oracle/src/backend/sql/indexes.js +++ b/plugins/dbgate-plugin-oracle/src/backend/sql/indexes.js @@ -8,7 +8,7 @@ select i.table_name as "tableName", ic.column_position as "postion", ic.descend as "descending" 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 i.index_name =OBJECT_ID_CONDITION order by i.table_owner, diff --git a/plugins/dbgate-plugin-oracle/src/backend/sql/matviewColumns.js b/plugins/dbgate-plugin-oracle/src/backend/sql/matviewColumns.js index 2049c1d3f..19545fc34 100644 --- a/plugins/dbgate-plugin-oracle/src/backend/sql/matviewColumns.js +++ b/plugins/dbgate-plugin-oracle/src/backend/sql/matviewColumns.js @@ -4,6 +4,6 @@ SELECT owner "schema_name" , column_name "column_name" , data_type "data_type" FROM all_tab_columns av - where table_name =OBJECT_ID_CONDITION + where OWNER = '$owner' AND table_name =OBJECT_ID_CONDITION order by column_id `; diff --git a/plugins/dbgate-plugin-oracle/src/backend/sql/matviews.js b/plugins/dbgate-plugin-oracle/src/backend/sql/matviews.js index 6504753d2..233ecc04f 100644 --- a/plugins/dbgate-plugin-oracle/src/backend/sql/matviews.js +++ b/plugins/dbgate-plugin-oracle/src/backend/sql/matviews.js @@ -14,6 +14,6 @@ SELECT owner as schema_name, '//text()' )) definition FROM all_mviews -where mview_name=OBJECT_ID_CONDITION +where OWNER = '$owner' AND mview_name=OBJECT_ID_CONDITION order by owner, mview_name `; \ No newline at end of file diff --git a/plugins/dbgate-plugin-oracle/src/backend/sql/routines.js b/plugins/dbgate-plugin-oracle/src/backend/sql/routines.js index c66004bd9..7385514e0 100644 --- a/plugins/dbgate-plugin-oracle/src/backend/sql/routines.js +++ b/plugins/dbgate-plugin-oracle/src/backend/sql/routines.js @@ -32,6 +32,7 @@ from (select all_procedures ap, all_objects ao where + ap.owner = '$owner' and ap.owner = ao.owner and ap.object_name = ao.object_name and ao.object_type in ('PACKAGE', 'PROCEDURE', 'FUNCTION') diff --git a/plugins/dbgate-plugin-oracle/src/backend/sql/tableList.js b/plugins/dbgate-plugin-oracle/src/backend/sql/tableList.js index 33ac51bbf..81e07e112 100644 --- a/plugins/dbgate-plugin-oracle/src/backend/sql/tableList.js +++ b/plugins/dbgate-plugin-oracle/src/backend/sql/tableList.js @@ -4,6 +4,6 @@ select table_name "pure_name" from all_tables - where TABLE_NAME =OBJECT_ID_CONDITION + where OWNER='$owner' AND TABLE_NAME =OBJECT_ID_CONDITION `; diff --git a/plugins/dbgate-plugin-oracle/src/backend/sql/uniqueNames.js b/plugins/dbgate-plugin-oracle/src/backend/sql/uniqueNames.js index 5832aa800..62a5b12d6 100644 --- a/plugins/dbgate-plugin-oracle/src/backend/sql/uniqueNames.js +++ b/plugins/dbgate-plugin-oracle/src/backend/sql/uniqueNames.js @@ -1,6 +1,6 @@ module.exports = ` select constraint_name from all_constraints -where constraint_type = 'U' +where OWNER='$owner' and constraint_type = 'U' and constraint_name =OBJECT_ID_CONDITION `; diff --git a/plugins/dbgate-plugin-oracle/src/backend/sql/views.js b/plugins/dbgate-plugin-oracle/src/backend/sql/views.js index 8e8b33d7a..9f10f7fb4 100644 --- a/plugins/dbgate-plugin-oracle/src/backend/sql/views.js +++ b/plugins/dbgate-plugin-oracle/src/backend/sql/views.js @@ -6,7 +6,7 @@ from (select owner as "schema_name", SUBSTR(text_vc, 1, 3900) AS "create_sql" from all_views av - where text_vc is not null + where owner = '$owner' and text_vc is not null ) avv where "pure_name" =OBJECT_ID_CONDITION `; diff --git a/plugins/dbgate-plugin-oracle/src/frontend/drivers.js b/plugins/dbgate-plugin-oracle/src/frontend/drivers.js index 4f253b57b..1c04f2a13 100644 --- a/plugins/dbgate-plugin-oracle/src/frontend/drivers.js +++ b/plugins/dbgate-plugin-oracle/src/frontend/drivers.js @@ -19,7 +19,6 @@ const dialect = { quoteIdentifier(s) { return '"' + s + '"'; }, - stringAgg: true, createColumn: true, dropColumn: true, @@ -110,32 +109,15 @@ const oracleDriverBase = { getQuerySplitterOptions: () => oracleSplitterOptions, readOnlySessions: true, - databaseUrlPlaceholder: 'e.g. oracledb://user:password@localhost:1521', + databaseUrlPlaceholder: 'e.g. localhost:1521/orcl', showConnectionField: (field, values) => { if (field == 'useDatabaseUrl') return true; if (values.useDatabaseUrl) { - return ['databaseUrl', 'isReadOnly'].includes(field); + return ['databaseUrl', 'user', 'password'].includes(field); } - return ['user', 'password', 'defaultDatabase', 'singleDatabase', 'isReadOnly', 'server', 'port'].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: '', + return ['user', 'password', 'server', 'port', 'serviceName'].includes(field); }, getNewObjectTemplates() { @@ -189,7 +171,7 @@ const oracleDriver = { return dialect; }, - showConnectionTab: (field) => field == 'sshTunnel', + showConnectionTab: field => field == 'sshTunnel', }; module.exports = [oracleDriver]; diff --git a/plugins/dbgate-plugin-postgres/src/backend/Analyser.js b/plugins/dbgate-plugin-postgres/src/backend/Analyser.js index 4ec6c674b..693d0cf10 100644 --- a/plugins/dbgate-plugin-postgres/src/backend/Analyser.js +++ b/plugins/dbgate-plugin-postgres/src/backend/Analyser.js @@ -70,29 +70,23 @@ class Analyser extends DatabaseAnalyser { const tables = await this.analyserQuery(this.driver.dialect.stringAgg ? 'tableModifications' : 'tableList', [ 'tables', ]); - this.logger.debug({ count: tables.rows.length }, 'Tables loaded'); this.feedback({ analysingMessage: 'Loading columns' }); const columns = await this.analyserQuery('columns', ['tables', 'views']); - this.logger.debug({ count: columns.rows.length }, 'Columns loaded'); this.feedback({ analysingMessage: 'Loading primary keys' }); const pkColumns = await this.analyserQuery('primaryKeys', ['tables']); - this.logger.debug({ count: pkColumns.rows.length }, 'Primary keys loaded'); let fkColumns = null; this.feedback({ analysingMessage: 'Loading foreign key constraints' }); 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' }); 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' }); 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 fkRows = []; @@ -134,50 +128,41 @@ class Analyser extends DatabaseAnalyser { this.feedback({ analysingMessage: 'Loading views' }); const views = await this.analyserQuery('views', ['views']); - this.logger.debug({ count: views.rows.length }, 'Views loaded'); this.feedback({ analysingMessage: 'Loading materialized views' }); 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' }); const matviewColumns = this.driver.dialect.materializedViews ? await this.analyserQuery('matviewColumns', ['matviews']) : null; - this.logger.debug({ count: matviewColumns.rows.length }, 'Materialized view columns loaded'); this.feedback({ analysingMessage: 'Loading routines' }); const routines = await this.analyserQuery('routines', ['procedures', 'functions']); - this.logger.debug({ count: routines.rows.length }, 'Routines loaded'); this.feedback({ analysingMessage: 'Loading indexes' }); const indexes = this.driver.__analyserInternals.skipIndexes ? { rows: [] } : await this.analyserQuery('indexes', ['tables']); - this.logger.debug({ count: indexes.rows.length }, 'Indexes loaded'); this.feedback({ analysingMessage: 'Loading index columns' }); const indexcols = this.driver.__analyserInternals.skipIndexes ? { rows: [] } : await this.analyserQuery('indexcols', ['tables']); - this.logger.debug({ count: indexcols.rows.length }, 'Indexes columns loaded'); this.feedback({ analysingMessage: 'Loading unique names' }); const uniqueNames = await this.analyserQuery('uniqueNames', ['tables']); - this.logger.debug({ count: uniqueNames.rows.length }, 'Uniques loaded'); let geometryColumns = { rows: [] }; if (views.rows.find(x => x.pure_name == 'geometry_columns' && x.schema_name == 'public')) { this.feedback({ analysingMessage: 'Loading geometry columns' }); geometryColumns = await this.analyserQuery('geometryColumns', ['tables']); - this.logger.debug({ count: geometryColumns.rows.length }, 'Geometry columns loaded'); } let geographyColumns = { rows: [] }; if (views.rows.find(x => x.pure_name == 'geography_columns' && x.schema_name == 'public')) { this.feedback({ analysingMessage: 'Loading geography columns' }); geographyColumns = await this.analyserQuery('geographyColumns', ['tables']); - this.logger.debug({ count: geographyColumns.rows.length }, 'Geography columns loaded'); } this.feedback({ analysingMessage: 'Finalizing DB structure' });