Merge branch 'develop'

This commit is contained in:
Jan Prochazka
2023-01-21 13:56:55 +01:00
13 changed files with 163 additions and 218 deletions

View File

@@ -1,6 +1,6 @@
{ {
"private": true, "private": true,
"version": "5.2.1", "version": "5.2.2-beta.1",
"name": "dbgate-all", "name": "dbgate-all",
"workspaces": [ "workspaces": [
"packages/*", "packages/*",

View File

@@ -170,9 +170,9 @@ export class DatabaseAnalyser {
// return this.structure.tables.find((x) => x.objectId == id); // return this.structure.tables.find((x) => x.objectId == id);
// } // }
containsObjectIdCondition(typeFields) { // containsObjectIdCondition(typeFields) {
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) {
return this.createQueryCore(template, typeFields); return this.createQueryCore(template, typeFields);
@@ -197,7 +197,7 @@ export class DatabaseAnalyser {
.filter(x => typeFields.includes(x.objectTypeField) && (x.action == 'add' || x.action == 'change')) .filter(x => typeFields.includes(x.objectTypeField) && (x.action == 'add' || x.action == 'change'))
.map(x => x.objectId); .map(x => x.objectId);
if (filterIds.length == 0) { if (filterIds.length == 0) {
return template.replace(/=OBJECT_ID_CONDITION/g, " = '0'"); return null;
} }
return template.replace(/=OBJECT_ID_CONDITION/g, ` in (${filterIds.map(x => `'${x}'`).join(',')})`); return template.replace(/=OBJECT_ID_CONDITION/g, ` in (${filterIds.map(x => `'${x}'`).join(',')})`);
} }
@@ -293,7 +293,14 @@ export class DatabaseAnalyser {
return [..._compact(res), ...this.getDeletedObjects(snapshot)]; return [..._compact(res), ...this.getDeletedObjects(snapshot)];
} }
async safeQuery(sql) { async analyserQuery(template, typeFields) {
const sql = this.createQuery(template, typeFields);
if (!sql) {
return {
rows: [],
};
}
try { try {
return await this.driver.query(this.pool, sql); return await this.driver.query(this.pool, sql);
} catch (err) { } catch (err) {

View File

@@ -82,33 +82,35 @@ class MsSqlAnalyser extends DatabaseAnalyser {
async _runAnalysis() { async _runAnalysis() {
this.feedback({ analysingMessage: 'Loading tables' }); this.feedback({ analysingMessage: 'Loading tables' });
const tablesRows = await this.driver.query(this.pool, this.createQuery('tables', ['tables'])); const tablesRows = await this.analyserQuery('tables', ['tables']);
this.feedback({ analysingMessage: 'Loading columns' }); this.feedback({ analysingMessage: 'Loading columns' });
const columnsRows = await this.driver.query(this.pool, this.createQuery('columns', ['tables'])); const columnsRows = await this.analyserQuery('columns', ['tables']);
this.feedback({ analysingMessage: 'Loading primary keys' }); this.feedback({ analysingMessage: 'Loading primary keys' });
const pkColumnsRows = await this.driver.query(this.pool, this.createQuery('primaryKeys', ['tables'])); const pkColumnsRows = await this.analyserQuery('primaryKeys', ['tables']);
this.feedback({ analysingMessage: 'Loading foreign keys' }); this.feedback({ analysingMessage: 'Loading foreign keys' });
const fkColumnsRows = await this.driver.query(this.pool, this.createQuery('foreignKeys', ['tables'])); const fkColumnsRows = await this.analyserQuery('foreignKeys', ['tables']);
this.feedback({ analysingMessage: 'Loading schemas' }); this.feedback({ analysingMessage: 'Loading schemas' });
const schemaRows = await this.driver.query(this.pool, this.createQuery('getSchemas')); const schemaRows = await this.analyserQuery('getSchemas');
this.feedback({ analysingMessage: 'Loading indexes' }); this.feedback({ analysingMessage: 'Loading indexes' });
const indexesRows = await this.driver.query(this.pool, this.createQuery('indexes', ['tables'])); const indexesRows = await this.analyserQuery('indexes', ['tables']);
this.feedback({ analysingMessage: 'Loading index columns' }); this.feedback({ analysingMessage: 'Loading index columns' });
const indexcolsRows = await this.driver.query(this.pool, this.createQuery('indexcols', ['tables'])); const indexcolsRows = await this.analyserQuery('indexcols', ['tables']);
this.feedback({ analysingMessage: 'Loading default schema' }); this.feedback({ analysingMessage: 'Loading default schema' });
const defaultSchemaRows = await this.driver.query(this.pool, 'SELECT SCHEMA_NAME() as name'); const defaultSchemaRows = await this.driver.query(this.pool, 'SELECT SCHEMA_NAME() as name');
this.feedback({ analysingMessage: 'Loading table sizes' }); this.feedback({ analysingMessage: 'Loading table sizes' });
const tableSizes = await this.driver.query(this.pool, this.createQuery('tableSizes')); const tableSizes = await this.analyserQuery('tableSizes');
const schemas = schemaRows.rows; const schemas = schemaRows.rows;
const tableSizesDict = _.mapValues(_.keyBy(tableSizes.rows, 'objectId'), 'tableRowCount'); const tableSizesDict = _.mapValues(_.keyBy(tableSizes.rows, 'objectId'), 'tableRowCount');
this.feedback({ analysingMessage: 'Loading SQL code' }); this.feedback({ analysingMessage: 'Loading SQL code' });
const sqlCodeRows = await this.driver.query( const sqlCodeRows = await this.analyserQuery('loadSqlCode', [
this.pool, 'views',
this.createQuery('loadSqlCode', ['views', 'procedures', 'functions', 'triggers']) 'procedures',
); 'functions',
'triggers',
]);
const getCreateSql = row => const getCreateSql = row =>
sqlCodeRows.rows sqlCodeRows.rows
.filter(x => x.pureName == row.pureName && x.schemaName == row.schemaName) .filter(x => x.pureName == row.pureName && x.schemaName == row.schemaName)
@@ -116,14 +118,11 @@ class MsSqlAnalyser extends DatabaseAnalyser {
.join(''); .join('');
this.feedback({ analysingMessage: 'Loading views' }); this.feedback({ analysingMessage: 'Loading views' });
const viewsRows = await this.driver.query(this.pool, this.createQuery('views', ['views'])); const viewsRows = await this.analyserQuery('views', ['views']);
this.feedback({ analysingMessage: 'Loading procedures & functions' }); this.feedback({ analysingMessage: 'Loading procedures & functions' });
const programmableRows = await this.driver.query( const programmableRows = await this.analyserQuery('programmables', ['procedures', 'functions']);
this.pool,
this.createQuery('programmables', ['procedures', 'functions'])
);
this.feedback({ analysingMessage: 'Loading view columns' }); this.feedback({ analysingMessage: 'Loading view columns' });
const viewColumnRows = await this.driver.query(this.pool, this.createQuery('viewColumns', ['views'])); const viewColumnRows = await this.analyserQuery('viewColumns', ['views']);
this.feedback({ analysingMessage: 'Finalizing DB structure' }); this.feedback({ analysingMessage: 'Finalizing DB structure' });
const tables = tablesRows.rows.map(row => ({ const tables = tablesRows.rows.map(row => ({
@@ -190,8 +189,8 @@ class MsSqlAnalyser extends DatabaseAnalyser {
} }
async _getFastSnapshot() { async _getFastSnapshot() {
const modificationsQueryData = await this.driver.query(this.pool, this.createQuery('modifications')); const modificationsQueryData = await this.analyserQuery('modifications');
const tableSizes = await this.driver.query(this.pool, this.createQuery('tableSizes')); const tableSizes = await this.analyserQuery('tableSizes');
const res = DatabaseAnalyser.createEmptyStructure(); const res = DatabaseAnalyser.createEmptyStructure();
for (const item of modificationsQueryData.rows) { for (const item of modificationsQueryData.rows) {

View File

@@ -57,7 +57,7 @@ class Analyser extends DatabaseAnalyser {
async getViewTexts(allViewNames) { async getViewTexts(allViewNames) {
const res = {}; const res = {};
const views = await this.safeQuery(this.createQuery('viewTexts', ['views'])); const views = await this.analyserQuery('viewTexts', ['views']);
for (const view of views.rows) { for (const view of views.rows) {
res[view.pureName] = `CREATE VIEW \`${view.pureName}\` AS ${view.viewDefinition}`; res[view.pureName] = `CREATE VIEW \`${view.pureName}\` AS ${view.viewDefinition}`;
} }
@@ -76,24 +76,24 @@ class Analyser extends DatabaseAnalyser {
async _runAnalysis() { async _runAnalysis() {
this.feedback({ analysingMessage: 'Loading tables' }); this.feedback({ analysingMessage: 'Loading tables' });
const tables = await this.driver.query(this.pool, this.createQuery('tables', ['tables'])); const tables = await this.analyserQuery('tables', ['tables']);
this.feedback({ analysingMessage: 'Loading columns' }); this.feedback({ analysingMessage: 'Loading columns' });
const columns = await this.driver.query(this.pool, this.createQuery('columns', ['tables', 'views'])); const columns = await this.analyserQuery('columns', ['tables', 'views']);
this.feedback({ analysingMessage: 'Loading primary keys' }); this.feedback({ analysingMessage: 'Loading primary keys' });
const pkColumns = await this.safeQuery(this.createQuery('primaryKeys', ['tables'])); const pkColumns = await this.analyserQuery('primaryKeys', ['tables']);
this.feedback({ analysingMessage: 'Loading foreign keys' }); this.feedback({ analysingMessage: 'Loading foreign keys' });
const fkColumns = await this.safeQuery(this.createQuery('foreignKeys', ['tables'])); const fkColumns = await this.analyserQuery('foreignKeys', ['tables']);
this.feedback({ analysingMessage: 'Loading views' }); this.feedback({ analysingMessage: 'Loading views' });
const views = await this.safeQuery(this.createQuery('views', ['views'])); const views = await this.analyserQuery('views', ['views']);
this.feedback({ analysingMessage: 'Loading programmables' }); this.feedback({ analysingMessage: 'Loading programmables' });
const programmables = await this.safeQuery(this.createQuery('programmables', ['procedures', 'functions'])); const programmables = await this.analyserQuery('programmables', ['procedures', 'functions']);
this.feedback({ analysingMessage: 'Loading view texts' }); this.feedback({ analysingMessage: 'Loading view texts' });
const viewTexts = await this.getViewTexts(views.rows.map(x => x.pureName)); const viewTexts = await this.getViewTexts(views.rows.map(x => x.pureName));
this.feedback({ analysingMessage: 'Loading indexes' }); this.feedback({ analysingMessage: 'Loading indexes' });
const indexes = await this.safeQuery(this.createQuery('indexes', ['tables'])); const indexes = await this.analyserQuery('indexes', ['tables']);
this.feedback({ analysingMessage: 'Loading uniques' }); this.feedback({ analysingMessage: 'Loading uniques' });
const uniqueNames = await this.safeQuery(this.createQuery('uniqueNames', ['tables'])); const uniqueNames = await this.analyserQuery('uniqueNames', ['tables']);
this.feedback({ analysingMessage: 'Finalizing DB structure' }); this.feedback({ analysingMessage: 'Finalizing DB structure' });
const res = { const res = {
@@ -169,15 +169,9 @@ class Analyser extends DatabaseAnalyser {
} }
async _getFastSnapshot() { async _getFastSnapshot() {
const tableModificationsQueryData = await this.driver.query(this.pool, this.createQuery('tableModifications')); const tableModificationsQueryData = await this.analyserQuery('tableModifications');
const procedureModificationsQueryData = await this.driver.query( const procedureModificationsQueryData = await this.analyserQuery('procedureModifications');
this.pool, const functionModificationsQueryData = await this.analyserQuery('functionModifications');
this.createQuery('procedureModifications')
);
const functionModificationsQueryData = await this.driver.query(
this.pool,
this.createQuery('functionModifications')
);
return { return {
tables: tableModificationsQueryData.rows tables: tableModificationsQueryData.rows

View File

@@ -68,45 +68,40 @@ class Analyser extends DatabaseAnalyser {
async _runAnalysis() { async _runAnalysis() {
this.feedback({ analysingMessage: 'Loading tables' }); this.feedback({ analysingMessage: 'Loading tables' });
const tables = await this.driver.query( const tables = await this.analyserQuery(this.driver.dialect.stringAgg ? 'tableList' : 'tableList', ['tables']);
this.pool,
this.createQuery(this.driver.dialect.stringAgg ? 'tableList' : 'tableList', ['tables'])
);
this.feedback({ analysingMessage: 'Loading columns' }); this.feedback({ analysingMessage: 'Loading columns' });
const columns = await this.driver.query(this.pool, this.createQuery('columns', ['tables', 'views'])); const columns = await this.analyserQuery('columns', ['tables', 'views']);
this.feedback({ analysingMessage: 'Loading primary keys' }); this.feedback({ analysingMessage: 'Loading primary keys' });
const pkColumns = await this.driver.query(this.pool, this.createQuery('primaryKeys', ['tables'])); const pkColumns = await this.analyserQuery('primaryKeys', ['tables']);
//let fkColumns = null; //let fkColumns = null;
this.feedback({ analysingMessage: 'Loading foreign keys' }); this.feedback({ analysingMessage: 'Loading foreign keys' });
const fkColumns = await this.driver.query(this.pool, this.createQuery('foreignKeys', ['tables'])); const fkColumns = await this.analyserQuery('foreignKeys', ['tables']);
this.feedback({ analysingMessage: 'Loading views' }); this.feedback({ analysingMessage: 'Loading views' });
const views = await this.driver.query(this.pool, this.createQuery('views', ['views'])); const views = await this.analyserQuery('views', ['views']);
let geometryColumns = { rows: [] }; let geometryColumns = { rows: [] };
let geographyColumns = { rows: [] }; let geographyColumns = { rows: [] };
this.feedback({ analysingMessage: 'Loading materialized views' }); this.feedback({ analysingMessage: 'Loading materialized views' });
const matviews = this.driver.dialect.materializedViews const matviews = this.driver.dialect.materializedViews ? await this.analyserQuery('matviews', ['matviews']) : null;
? await this.driver.query(this.pool, this.createQuery('matviews', ['matviews']))
: null;
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.driver.query(this.pool, this.createQuery('matviewColumns', ['matviews'])) ? await this.analyserQuery('matviewColumns', ['matviews'])
: null; : null;
this.feedback({ analysingMessage: 'Loading routines' }); this.feedback({ analysingMessage: 'Loading routines' });
const routines = await this.driver.query(this.pool, this.createQuery('routines', ['procedures', 'functions'])); const routines = await this.analyserQuery('routines', ['procedures', 'functions']);
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.driver.query(this.pool, this.createQuery('indexes', ['tables'])); : await this.analyserQuery('indexes', ['tables']);
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.driver.query(this.pool, this.createQuery('indexcols', ['tables'])); // : 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.driver.query(this.pool, this.createQuery('uniqueNames', ['tables'])); const uniqueNames = await this.analyserQuery('uniqueNames', ['tables']);
this.feedback({ analysingMessage: 'Finalizing DB structure' }); this.feedback({ analysingMessage: 'Finalizing DB structure' });
const columnColumnsMapped = fkColumns.rows.map(x => ({ const columnColumnsMapped = fkColumns.rows.map(x => ({
@@ -144,34 +139,35 @@ class Analyser extends DatabaseAnalyser {
.map(col => getColumnInfo(col, newTable, geometryColumns, geographyColumns)), .map(col => getColumnInfo(col, newTable, geometryColumns, geographyColumns)),
primaryKey: DatabaseAnalyser.extractPrimaryKeys(newTable, pkColumnsMapped), primaryKey: DatabaseAnalyser.extractPrimaryKeys(newTable, pkColumnsMapped),
foreignKeys: DatabaseAnalyser.extractForeignKeys(newTable, columnColumnsMapped), foreignKeys: DatabaseAnalyser.extractForeignKeys(newTable, columnColumnsMapped),
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 == table.pureName && !uniqueNames.rows.find(x => x.constraintName == idx.constraintName)
), ),
'constraintName' 'constraintName'
).map(idx => ({ ).map(idx => ({
..._.pick(idx, ['constraintName', 'indexType']), ..._.pick(idx, ['constraintName', 'indexType']),
isUnique: idx.Unique === 'UNIQUE', isUnique: idx.Unique === 'UNIQUE',
columns: indexes.rows columns: indexes.rows
.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']),
})), })),
})), })),
uniques: _.uniqBy( uniques: _.uniqBy(
indexes.rows.filter( indexes.rows.filter(
idx => idx.tableName == table.pureName && uniqueNames.rows.find(x => x.constraintName == idx.constraintName) idx =>
), idx.tableName == table.pureName && uniqueNames.rows.find(x => x.constraintName == idx.constraintName)
'constraintName' ),
).map(idx => ({ 'constraintName'
..._.pick(idx, ['constraintName']), ).map(idx => ({
columns: indexes.rows ..._.pick(idx, ['constraintName']),
.filter(col => col.tableName == idx.tableName && col.constraintName == idx.constraintName) columns: indexes.rows
.map(col => ({ .filter(col => col.tableName == idx.tableName && col.constraintName == idx.constraintName)
..._.pick(col, ['columnName']), .map(col => ({
})), ..._.pick(col, ['columnName']),
})), })),
})),
}; };
}), }),
views: views.rows.map(view => ({ views: views.rows.map(view => ({
@@ -225,13 +221,13 @@ class Analyser extends DatabaseAnalyser {
return null; return null;
const tableModificationsQueryData = this.driver.dialect.stringAgg const tableModificationsQueryData = this.driver.dialect.stringAgg
? await this.driver.query(this.pool, this.createQuery('tableModifications')) ? await this.analyserQuery('tableModifications')
: null; : null;
const viewModificationsQueryData = await this.driver.query(this.pool, this.createQuery('viewModifications')); const viewModificationsQueryData = await this.analyserQuery('viewModifications');
const matviewModificationsQueryData = this.driver.dialect.materializedViews const matviewModificationsQueryData = this.driver.dialect.materializedViews
? await this.driver.query(this.pool, this.createQuery('matviewModifications')) ? await this.analyserQuery('matviewModifications')
: null; : null;
const routineModificationsQueryData = await this.driver.query(this.pool, this.createQuery('routineModifications')); const routineModificationsQueryData = await this.analyserQuery('routineModifications');
return { return {
tables: tableModificationsQueryData tables: tableModificationsQueryData
@@ -243,13 +239,13 @@ class Analyser extends DatabaseAnalyser {
})) }))
: null, : null,
views: viewModificationsQueryData views: viewModificationsQueryData
? viewModificationsQueryData.rows.map(x => ({ ? viewModificationsQueryData.rows.map(x => ({
objectId: `views:${x.schema_name}.${x.pure_name}`, objectId: `views:${x.schema_name}.${x.pure_name}`,
pureName: x.pure_name, pureName: x.pure_name,
schemaName: x.schema_name, schemaName: x.schema_name,
contentHash: x.hash_code, contentHash: x.hash_code,
})) }))
: undefined, : undefined,
matviews: matviewModificationsQueryData matviews: matviewModificationsQueryData
? matviewModificationsQueryData.rows.map(x => ({ ? matviewModificationsQueryData.rows.map(x => ({
objectId: `matviews:${x.schema_name}.${x.pure_name}`, objectId: `matviews:${x.schema_name}.${x.pure_name}`,

View File

@@ -57,8 +57,7 @@ class Analyser extends DatabaseAnalyser {
createQuery(resFileName, typeFields) { createQuery(resFileName, typeFields) {
const query = super.createQuery(sql[resFileName], typeFields); const query = super.createQuery(sql[resFileName], typeFields);
if (query) return query.replace('#REFTABLECOND#', this.driver.__analyserInternals.refTableCond); return query;
return null;
} }
async _computeSingleObjectId() { async _computeSingleObjectId() {
@@ -68,108 +67,92 @@ class Analyser extends DatabaseAnalyser {
async _runAnalysis() { async _runAnalysis() {
this.feedback({ analysingMessage: 'Loading tables' }); this.feedback({ analysingMessage: 'Loading tables' });
const tables = await this.driver.query( const tables = await this.analyserQuery(this.driver.dialect.stringAgg ? 'tableModifications' : 'tableList', [
this.pool, 'tables',
this.createQuery(this.driver.dialect.stringAgg ? 'tableModifications' : 'tableList', ['tables']) ]);
);
this.feedback({ analysingMessage: 'Loading columns' }); this.feedback({ analysingMessage: 'Loading columns' });
const columns = await this.driver.query(this.pool, this.createQuery('columns', ['tables', 'views'])); const columns = await this.analyserQuery('columns', ['tables', 'views']);
this.feedback({ analysingMessage: 'Loading primary keys' }); this.feedback({ analysingMessage: 'Loading primary keys' });
const pkColumns = await this.driver.query(this.pool, this.createQuery('primaryKeys', ['tables'])); const pkColumns = await this.analyserQuery('primaryKeys', ['tables']);
let fkColumns = null; let fkColumns = null;
// if (true) { this.feedback({ analysingMessage: 'Loading foreign key constraints' });
if (this.containsObjectIdCondition(['tables']) || this.driver.__analyserInternals.refTableCond) { const fk_tableConstraints = await this.analyserQuery('fk_tableConstraints', ['tables']);
this.feedback({ analysingMessage: 'Loading foreign keys' });
fkColumns = await this.driver.query(this.pool, this.createQuery('foreignKeys', ['tables'])); this.feedback({ analysingMessage: 'Loading foreign key refs' });
} else { const fk_referentialConstraints = await this.analyserQuery('fk_referentialConstraints', ['tables']);
this.feedback({ analysingMessage: 'Loading foreign key constraints' }); this.feedback({ analysingMessage: 'Loading foreign key columns' });
const fk_tableConstraints = await this.driver.query( const fk_keyColumnUsage = await this.analyserQuery('fk_keyColumnUsage', ['tables']);
this.pool,
this.createQuery('fk_tableConstraints', ['tables']) const cntKey = x => `${x.constraint_name}|${x.constraint_schema}`;
const fkRows = [];
const fkConstraintDct = _.keyBy(fk_tableConstraints.rows, cntKey);
for (const fkRef of fk_referentialConstraints.rows) {
const cntBase = fkConstraintDct[cntKey(fkRef)];
const cntRef = fkConstraintDct[`${fkRef.unique_constraint_name}|${fkRef.unique_constraint_schema}`];
if (!cntBase || !cntRef) continue;
const baseCols = _.sortBy(
fk_keyColumnUsage.rows.filter(
x => x.table_name == cntBase.table_name && x.constraint_name == cntBase.constraint_name
),
'ordinal_position'
); );
const refCols = _.sortBy(
this.feedback({ analysingMessage: 'Loading foreign key refs' }); fk_keyColumnUsage.rows.filter(
const fk_referentialConstraints = await this.driver.query( x => x.table_name == cntRef.table_name && x.constraint_name == cntRef.constraint_name
this.pool, ),
this.createQuery('fk_referentialConstraints', ['tables']) 'ordinal_position'
); );
if (baseCols.length != refCols.length) continue;
this.feedback({ analysingMessage: 'Loading foreign key columns' }); for (let i = 0; i < baseCols.length; i++) {
const fk_keyColumnUsage = await this.driver.query(this.pool, this.createQuery('fk_keyColumnUsage', ['tables'])); const baseCol = baseCols[i];
const refCol = refCols[i];
const cntKey = x => `${x.constraint_name}|${x.constraint_schema}`; fkRows.push({
const rows = []; ...fkRef,
const constraintDct = _.keyBy(fk_tableConstraints.rows, cntKey); pure_name: cntBase.table_name,
for (const fkRef of fk_referentialConstraints.rows) { schema_name: cntBase.table_schema,
const cntBase = constraintDct[cntKey(fkRef)]; ref_table_name: cntRef.table_name,
const cntRef = constraintDct[`${fkRef.unique_constraint_name}|${fkRef.unique_constraint_schema}`]; ref_schema_name: cntRef.table_schema,
if (!cntBase || !cntRef) continue; column_name: baseCol.column_name,
const baseCols = _.sortBy( ref_column_name: refCol.column_name,
fk_keyColumnUsage.rows.filter( });
x => x.table_name == cntBase.table_name && x.constraint_name == cntBase.constraint_name
),
'ordinal_position'
);
const refCols = _.sortBy(
fk_keyColumnUsage.rows.filter(
x => x.table_name == cntRef.table_name && x.constraint_name == cntRef.constraint_name
),
'ordinal_position'
);
if (baseCols.length != refCols.length) continue;
for (let i = 0; i < baseCols.length; i++) {
const baseCol = baseCols[i];
const refCol = refCols[i];
rows.push({
...fkRef,
pure_name: cntBase.table_name,
schema_name: cntBase.table_schema,
ref_table_name: cntRef.table_name,
ref_schema_name: cntRef.table_schema,
column_name: baseCol.column_name,
ref_column_name: refCol.column_name,
});
}
} }
fkColumns = { rows };
} }
fkColumns = { rows: fkRows };
this.feedback({ analysingMessage: 'Loading views' }); this.feedback({ analysingMessage: 'Loading views' });
const views = await this.driver.query(this.pool, this.createQuery('views', ['views'])); const views = await this.analyserQuery('views', ['views']);
this.feedback({ analysingMessage: 'Loading materialized views' }); this.feedback({ analysingMessage: 'Loading materialized views' });
const matviews = this.driver.dialect.materializedViews const matviews = this.driver.dialect.materializedViews ? await this.analyserQuery('matviews', ['matviews']) : null;
? await this.driver.query(this.pool, this.createQuery('matviews', ['matviews']))
: null;
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.driver.query(this.pool, this.createQuery('matviewColumns', ['matviews'])) ? await this.analyserQuery('matviewColumns', ['matviews'])
: null; : null;
this.feedback({ analysingMessage: 'Loading routines' }); this.feedback({ analysingMessage: 'Loading routines' });
const routines = await this.driver.query(this.pool, this.createQuery('routines', ['procedures', 'functions'])); const routines = await this.analyserQuery('routines', ['procedures', 'functions']);
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.driver.query(this.pool, this.createQuery('indexes', ['tables'])); : await this.analyserQuery('indexes', ['tables']);
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.driver.query(this.pool, this.createQuery('indexcols', ['tables'])); : await this.analyserQuery('indexcols', ['tables']);
this.feedback({ analysingMessage: 'Loading unique names' }); this.feedback({ analysingMessage: 'Loading unique names' });
const uniqueNames = await this.driver.query(this.pool, this.createQuery('uniqueNames', ['tables'])); const uniqueNames = await this.analyserQuery('uniqueNames', ['tables']);
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.safeQuery(this.createQuery('geometryColumns', ['tables'])); geometryColumns = await this.analyserQuery('geometryColumns', ['tables']);
} }
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.safeQuery(this.createQuery('geographyColumns', ['tables'])); geographyColumns = await this.analyserQuery('geographyColumns', ['tables']);
} }
this.feedback({ analysingMessage: 'Finalizing DB structure' }); this.feedback({ analysingMessage: 'Finalizing DB structure' });
@@ -299,13 +282,13 @@ class Analyser extends DatabaseAnalyser {
async _getFastSnapshot() { async _getFastSnapshot() {
const tableModificationsQueryData = this.driver.dialect.stringAgg const tableModificationsQueryData = this.driver.dialect.stringAgg
? await this.driver.query(this.pool, this.createQuery('tableModifications')) ? await this.analyserQuery('tableModifications')
: null; : null;
const viewModificationsQueryData = await this.driver.query(this.pool, this.createQuery('viewModifications')); const viewModificationsQueryData = await this.analyserQuery('viewModifications');
const matviewModificationsQueryData = this.driver.dialect.materializedViews const matviewModificationsQueryData = this.driver.dialect.materializedViews
? await this.driver.query(this.pool, this.createQuery('matviewModifications')) ? await this.analyserQuery('matviewModifications')
: null; : null;
const routineModificationsQueryData = await this.driver.query(this.pool, this.createQuery('routineModifications')); const routineModificationsQueryData = await this.analyserQuery('routineModifications');
return { return {
tables: tableModificationsQueryData tables: tableModificationsQueryData

View File

@@ -78,8 +78,6 @@ const drivers = driverBases.map(driverBase => ({
}; };
} }
console.log('OPTIONS', options);
const client = new pg.Client(options); const client = new pg.Client(options);
await client.connect(); await client.connect();

View File

@@ -7,4 +7,5 @@ select
basecol.table_name, basecol.table_name,
basecol.ordinal_position basecol.ordinal_position
from information_schema.key_column_usage basecol from information_schema.key_column_usage basecol
where ('tables:' || basecol.table_schema || '.' || basecol.table_name) =OBJECT_ID_CONDITION
`; `;

View File

@@ -5,4 +5,5 @@ select
base.constraint_name as "constraint_name", base.constraint_name as "constraint_name",
base.constraint_schema as "constraint_schema" base.constraint_schema as "constraint_schema"
from information_schema.table_constraints base from information_schema.table_constraints base
where ('tables:' || base.table_schema || '.' || base.table_name) =OBJECT_ID_CONDITION
`; `;

View File

@@ -1,24 +0,0 @@
module.exports = `
select
fk.constraint_name as "constraint_name",
fk.constraint_schema as "constraint_schema",
base.table_name as "pure_name",
base.table_schema as "schema_name",
fk.update_rule as "update_action",
fk.delete_rule as "delete_action",
ref.table_name as "ref_table_name",
ref.table_schema as "ref_schema_name",
basecol.column_name as "column_name",
refcol.column_name as "ref_column_name"
from information_schema.referential_constraints fk
inner join information_schema.table_constraints base on fk.constraint_name = base.constraint_name and fk.constraint_schema = base.constraint_schema
inner join information_schema.table_constraints ref on fk.unique_constraint_name = ref.constraint_name and fk.unique_constraint_schema = ref.constraint_schema #REFTABLECOND#
inner join information_schema.key_column_usage basecol on base.table_name = basecol.table_name and base.constraint_name = basecol.constraint_name
inner join information_schema.key_column_usage refcol on ref.table_name = refcol.table_name and ref.constraint_name = refcol.constraint_name and basecol.ordinal_position = refcol.ordinal_position
where
base.table_schema <> 'information_schema'
and base.table_schema <> 'pg_catalog'
and base.table_schema !~ '^pg_toast'
and ('tables:' || base.table_schema || '.' || base.table_name) =OBJECT_ID_CONDITION
order by basecol.ordinal_position
`;

View File

@@ -4,7 +4,6 @@ const tableList = require('./tableList');
const viewModifications = require('./viewModifications'); const viewModifications = require('./viewModifications');
const matviewModifications = require('./matviewModifications'); const matviewModifications = require('./matviewModifications');
const primaryKeys = require('./primaryKeys'); const primaryKeys = require('./primaryKeys');
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');
@@ -26,7 +25,6 @@ module.exports = {
tableList, tableList,
viewModifications, viewModifications,
primaryKeys, primaryKeys,
foreignKeys,
fk_keyColumnUsage, fk_keyColumnUsage,
fk_referentialConstraints, fk_referentialConstraints,
fk_tableConstraints, fk_tableConstraints,

View File

@@ -140,9 +140,7 @@ const postgresDriverBase = {
return connection; return connection;
}, },
__analyserInternals: { __analyserInternals: {},
refTableCond: '',
},
getNewObjectTemplates() { getNewObjectTemplates() {
return [ return [
@@ -212,9 +210,7 @@ const cockroachDriver = {
dropColumnDependencies: ['primaryKey', 'dependencies'], dropColumnDependencies: ['primaryKey', 'dependencies'],
dropPrimaryKey: false, dropPrimaryKey: false,
}, },
__analyserInternals: { __analyserInternals: {},
refTableCond: 'and fk.referenced_table_name = ref.table_name',
},
}; };
/** @type {import('dbgate-types').EngineDriver} */ /** @type {import('dbgate-types').EngineDriver} */
@@ -225,7 +221,6 @@ const redshiftDriver = {
stringAgg: false, stringAgg: false,
}, },
__analyserInternals: { __analyserInternals: {
refTableCond: '',
skipIndexes: true, skipIndexes: true,
}, },
engine: 'redshift@dbgate-plugin-postgres', engine: 'redshift@dbgate-plugin-postgres',

View File

@@ -53,12 +53,9 @@ class Analyser extends DatabaseAnalyser {
} }
async _runAnalysis() { async _runAnalysis() {
const objects = await this.driver.query( const objects = await this.analyserQuery(
this.pool, "select * from sqlite_master where (type='table' or type='view') and name =OBJECT_ID_CONDITION",
super.createQuery( ['tables', 'views']
"select * from sqlite_master where (type='table' or type='view') and name =OBJECT_ID_CONDITION",
['tables', 'views']
)
); );
const tables = objects.rows.filter((x) => x.type == 'table'); const tables = objects.rows.filter((x) => x.type == 'table');
const views = objects.rows.filter((x) => x.type == 'view'); const views = objects.rows.filter((x) => x.type == 'view');