mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-28 04:56:00 +00:00
postgresql materialized views #123
This commit is contained in:
@@ -32,7 +32,7 @@ describe('Object analyse', () => {
|
|||||||
const structure = await driver.analyseFull(conn);
|
const structure = await driver.analyseFull(conn);
|
||||||
|
|
||||||
expect(structure[type].length).toEqual(1);
|
expect(structure[type].length).toEqual(1);
|
||||||
expect(structure[type][0]).toEqual(type == 'views' ? view1Match : obj1Match);
|
expect(structure[type][0]).toEqual(type.includes('views') ? view1Match : obj1Match);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ describe('Object analyse', () => {
|
|||||||
const structure2 = await driver.analyseIncremental(conn, structure1);
|
const structure2 = await driver.analyseIncremental(conn, structure1);
|
||||||
|
|
||||||
expect(structure2[type].length).toEqual(2);
|
expect(structure2[type].length).toEqual(2);
|
||||||
expect(structure2[type].find(x => x.pureName == 'obj1')).toEqual(type == 'views' ? view1Match : obj1Match);
|
expect(structure2[type].find(x => x.pureName == 'obj1')).toEqual(type.includes('views') ? view1Match : obj1Match);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ describe('Object analyse', () => {
|
|||||||
const structure2 = await driver.analyseIncremental(conn, structure1);
|
const structure2 = await driver.analyseIncremental(conn, structure1);
|
||||||
|
|
||||||
expect(structure2[type].length).toEqual(1);
|
expect(structure2[type].length).toEqual(1);
|
||||||
expect(structure2[type][0]).toEqual(type == 'views' ? view1Match : obj1Match);
|
expect(structure2[type][0]).toEqual(type.includes('views') ? view1Match : obj1Match);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ describe('Object analyse', () => {
|
|||||||
const structure3 = await driver.analyseIncremental(conn, structure2);
|
const structure3 = await driver.analyseIncremental(conn, structure2);
|
||||||
|
|
||||||
expect(structure3[type].length).toEqual(1);
|
expect(structure3[type].length).toEqual(1);
|
||||||
expect(structure3[type][0]).toEqual(type == 'views' ? view1Match : obj1Match);
|
expect(structure3[type][0]).toEqual(type.includes('views') ? view1Match : obj1Match);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,6 +5,13 @@ const views = {
|
|||||||
drop1: 'DROP VIEW obj1',
|
drop1: 'DROP VIEW obj1',
|
||||||
drop2: 'DROP VIEW obj2',
|
drop2: 'DROP VIEW obj2',
|
||||||
};
|
};
|
||||||
|
const matviews = {
|
||||||
|
type: 'matviews',
|
||||||
|
create1: 'CREATE MATERIALIZED VIEW obj1 AS SELECT id FROM t1',
|
||||||
|
create2: 'CREATE MATERIALIZED VIEW obj2 AS SELECT id FROM t2',
|
||||||
|
drop1: 'DROP MATERIALIZED VIEW obj1',
|
||||||
|
drop2: 'DROP MATERIALIZED VIEW obj2',
|
||||||
|
};
|
||||||
|
|
||||||
const engines = [
|
const engines = [
|
||||||
{
|
{
|
||||||
@@ -39,6 +46,7 @@ const engines = [
|
|||||||
},
|
},
|
||||||
objects: [
|
objects: [
|
||||||
views,
|
views,
|
||||||
|
matviews,
|
||||||
{
|
{
|
||||||
type: 'procedures',
|
type: 'procedures',
|
||||||
create1: 'CREATE PROCEDURE obj1() LANGUAGE SQL AS $$ select * from t1 $$',
|
create1: 'CREATE PROCEDURE obj1() LANGUAGE SQL AS $$ select * from t1 $$',
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ function requirePlugin(packageName, requiredPlugin = null) {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
module = __non_webpack_require__(modulePath);
|
module = __non_webpack_require__(modulePath);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('Failed load webpacked module', err.message);
|
// console.log('Failed load webpacked module', err.message);
|
||||||
module = require(modulePath);
|
module = require(modulePath);
|
||||||
}
|
}
|
||||||
requiredPlugin = module.__esModule ? module.default : module;
|
requiredPlugin = module.__esModule ? module.default : module;
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export class DatabaseAnalyser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const res = {};
|
const res = {};
|
||||||
for (const field of ['tables', 'collections', 'views', 'functions', 'procedures', 'triggers']) {
|
for (const field of ['tables', 'collections', 'views', 'matviews', 'functions', 'procedures', 'triggers']) {
|
||||||
const removedIds = this.modifications
|
const removedIds = this.modifications
|
||||||
.filter(x => x.action == 'remove' && x.objectTypeField == field)
|
.filter(x => x.action == 'remove' && x.objectTypeField == field)
|
||||||
.map(x => x.objectId);
|
.map(x => x.objectId);
|
||||||
@@ -159,6 +159,7 @@ export class DatabaseAnalyser {
|
|||||||
...this.getDeletedObjectsForField(snapshot, 'tables'),
|
...this.getDeletedObjectsForField(snapshot, 'tables'),
|
||||||
...this.getDeletedObjectsForField(snapshot, 'collections'),
|
...this.getDeletedObjectsForField(snapshot, 'collections'),
|
||||||
...this.getDeletedObjectsForField(snapshot, 'views'),
|
...this.getDeletedObjectsForField(snapshot, 'views'),
|
||||||
|
...this.getDeletedObjectsForField(snapshot, 'matviews'),
|
||||||
...this.getDeletedObjectsForField(snapshot, 'procedures'),
|
...this.getDeletedObjectsForField(snapshot, 'procedures'),
|
||||||
...this.getDeletedObjectsForField(snapshot, 'functions'),
|
...this.getDeletedObjectsForField(snapshot, 'functions'),
|
||||||
...this.getDeletedObjectsForField(snapshot, 'triggers'),
|
...this.getDeletedObjectsForField(snapshot, 'triggers'),
|
||||||
@@ -179,6 +180,10 @@ export class DatabaseAnalyser {
|
|||||||
res.push({ objectTypeField: field, action: 'all' });
|
res.push({ objectTypeField: field, action: 'all' });
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (items === undefined) {
|
||||||
|
// skip - undefined meens, that field is not supported
|
||||||
|
continue;
|
||||||
|
}
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
const { objectId, schemaName, pureName, contentHash } = item;
|
const { objectId, schemaName, pureName, contentHash } = item;
|
||||||
const obj = this.structure[field].find(x => x.objectId == objectId);
|
const obj = this.structure[field].find(x => x.objectId == objectId);
|
||||||
@@ -211,6 +216,7 @@ export class DatabaseAnalyser {
|
|||||||
tables: [],
|
tables: [],
|
||||||
collections: [],
|
collections: [],
|
||||||
views: [],
|
views: [],
|
||||||
|
matviews: [],
|
||||||
functions: [],
|
functions: [],
|
||||||
procedures: [],
|
procedures: [],
|
||||||
triggers: [],
|
triggers: [],
|
||||||
|
|||||||
@@ -64,6 +64,10 @@ function fillTableExtendedInfo(db: DatabaseInfo): DatabaseInfo {
|
|||||||
...obj,
|
...obj,
|
||||||
objectTypeField: 'views',
|
objectTypeField: 'views',
|
||||||
})),
|
})),
|
||||||
|
matviews: (db.matviews || []).map(obj => ({
|
||||||
|
...obj,
|
||||||
|
objectTypeField: 'matviews',
|
||||||
|
})),
|
||||||
procedures: (db.procedures || []).map(obj => ({
|
procedures: (db.procedures || []).map(obj => ({
|
||||||
...obj,
|
...obj,
|
||||||
objectTypeField: 'procedures',
|
objectTypeField: 'procedures',
|
||||||
|
|||||||
1
packages/types/dbinfo.d.ts
vendored
1
packages/types/dbinfo.d.ts
vendored
@@ -95,6 +95,7 @@ export interface DatabaseInfoObjects {
|
|||||||
tables: TableInfo[];
|
tables: TableInfo[];
|
||||||
collections: CollectionInfo[];
|
collections: CollectionInfo[];
|
||||||
views: ViewInfo[];
|
views: ViewInfo[];
|
||||||
|
matviews: ViewInfo[];
|
||||||
procedures: ProcedureInfo[];
|
procedures: ProcedureInfo[];
|
||||||
functions: FunctionInfo[];
|
functions: FunctionInfo[];
|
||||||
triggers: TriggerInfo[];
|
triggers: TriggerInfo[];
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
tables: 'img table',
|
tables: 'img table',
|
||||||
collections: 'img collection',
|
collections: 'img collection',
|
||||||
views: 'img view',
|
views: 'img view',
|
||||||
|
matviews: 'img view',
|
||||||
procedures: 'img procedure',
|
procedures: 'img procedure',
|
||||||
functions: 'img function',
|
functions: 'img function',
|
||||||
};
|
};
|
||||||
@@ -14,6 +15,7 @@
|
|||||||
tables: 'TableDataTab',
|
tables: 'TableDataTab',
|
||||||
collections: 'CollectionDataTab',
|
collections: 'CollectionDataTab',
|
||||||
views: 'ViewDataTab',
|
views: 'ViewDataTab',
|
||||||
|
matviews: 'ViewDataTab',
|
||||||
};
|
};
|
||||||
|
|
||||||
const menus = {
|
const menus = {
|
||||||
@@ -146,6 +148,63 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
matviews: [
|
||||||
|
{
|
||||||
|
label: 'Open data',
|
||||||
|
tab: 'ViewDataTab',
|
||||||
|
forceNewTab: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Open structure',
|
||||||
|
tab: 'TableStructureTab',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Query designer',
|
||||||
|
isQueryDesigner: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
divider: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Export',
|
||||||
|
isExport: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Open in free table editor',
|
||||||
|
isOpenFreeTable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Open active chart',
|
||||||
|
isActiveChart: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
divider: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'SQL: CREATE MATERIALIZED VIEW',
|
||||||
|
scriptTemplate: 'CREATE OBJECT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'SQL: CREATE TABLE',
|
||||||
|
scriptTemplate: 'CREATE TABLE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'SQL: SELECT',
|
||||||
|
scriptTemplate: 'SELECT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'SQL Generator: CREATE MATERIALIZED VIEW',
|
||||||
|
sqlGeneratorProps: {
|
||||||
|
createMatviews: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'SQL Generator: DROP MATERIALIZED VIEW',
|
||||||
|
sqlGeneratorProps: {
|
||||||
|
dropMatviews: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
procedures: [
|
procedures: [
|
||||||
{
|
{
|
||||||
label: 'SQL: CREATE PROCEDURE',
|
label: 'SQL: CREATE PROCEDURE',
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
createTables: true,
|
createTables: true,
|
||||||
createForeignKeys: true,
|
createForeignKeys: true,
|
||||||
createViews: true,
|
createViews: true,
|
||||||
|
createMatviews: true,
|
||||||
createProcedures: true,
|
createProcedures: true,
|
||||||
createFunctions: true,
|
createFunctions: true,
|
||||||
createTriggers: true,
|
createTriggers: true,
|
||||||
@@ -48,6 +49,8 @@
|
|||||||
|
|
||||||
export let initialObjects = null;
|
export let initialObjects = null;
|
||||||
|
|
||||||
|
const OBJ_TYPE_LABELS = { Matview: 'Materialized view' };
|
||||||
|
|
||||||
let busy = false;
|
let busy = false;
|
||||||
let managerSize;
|
let managerSize;
|
||||||
let objectsFilter = '';
|
let objectsFilter = '';
|
||||||
@@ -72,7 +75,7 @@
|
|||||||
$: generatePreview($valuesStore, $checkedObjectsStore);
|
$: generatePreview($valuesStore, $checkedObjectsStore);
|
||||||
|
|
||||||
$: objectList = _.flatten(
|
$: objectList = _.flatten(
|
||||||
['tables', 'views', 'procedures', 'functions'].map(objectTypeField =>
|
['tables', 'views', 'matviews', 'procedures', 'functions'].map(objectTypeField =>
|
||||||
_.sortBy(
|
_.sortBy(
|
||||||
(($dbinfo || {})[objectTypeField] || []).map(obj => ({ ...obj, objectTypeField })),
|
(($dbinfo || {})[objectTypeField] || []).map(obj => ({ ...obj, objectTypeField })),
|
||||||
['schemaName', 'pureName']
|
['schemaName', 'pureName']
|
||||||
@@ -125,6 +128,7 @@
|
|||||||
);
|
);
|
||||||
closeCurrentModal();
|
closeCurrentModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<FormProviderCore values={valuesStore} template={FormFieldTemplateTiny}>
|
<FormProviderCore values={valuesStore} template={FormFieldTemplateTiny}>
|
||||||
@@ -211,8 +215,8 @@
|
|||||||
|
|
||||||
<FormCheckboxField label="Truncate tables (delete all rows)" name="truncate" />
|
<FormCheckboxField label="Truncate tables (delete all rows)" name="truncate" />
|
||||||
|
|
||||||
{#each ['View', 'Procedure', 'Function', 'Trigger'] as objtype}
|
{#each ['View', 'MatView', 'Procedure', 'Function', 'Trigger'] as objtype}
|
||||||
<div class="obj-heading">{objtype}s</div>
|
<div class="obj-heading">{OBJ_TYPE_LABELS[objtype] || objtype}s</div>
|
||||||
<FormCheckboxField label="Create" name={`create${objtype}s`} />
|
<FormCheckboxField label="Create" name={`create${objtype}s`} />
|
||||||
<FormCheckboxField label="Drop" name={`drop${objtype}s`} />
|
<FormCheckboxField label="Drop" name={`drop${objtype}s`} />
|
||||||
{#if values[`drop${objtype}s`]}
|
{#if values[`drop${objtype}s`]}
|
||||||
@@ -254,4 +258,5 @@
|
|||||||
.dbname {
|
.dbname {
|
||||||
color: var(--theme-font-3);
|
color: var(--theme-font-3);
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -260,9 +260,18 @@ export function useDbCore(args, objectTypeField = undefined) {
|
|||||||
if (!dbStore) return null;
|
if (!dbStore) return null;
|
||||||
return derived(dbStore, db => {
|
return derived(dbStore, db => {
|
||||||
if (!db) return null;
|
if (!db) return null;
|
||||||
|
if (_.isArray(objectTypeField)) {
|
||||||
|
for (const field of objectTypeField) {
|
||||||
|
const res = db[field || args.objectTypeField].find(
|
||||||
|
x => x.pureName == args.pureName && x.schemaName == args.schemaName
|
||||||
|
);
|
||||||
|
if (res) return res;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
return db[objectTypeField || args.objectTypeField].find(
|
return db[objectTypeField || args.objectTypeField].find(
|
||||||
x => x.pureName == args.pureName && x.schemaName == args.schemaName
|
x => x.pureName == args.pureName && x.schemaName == args.schemaName
|
||||||
);
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,7 +292,7 @@ export function getViewInfo(args) {
|
|||||||
|
|
||||||
/** @returns {import('dbgate-types').ViewInfo} */
|
/** @returns {import('dbgate-types').ViewInfo} */
|
||||||
export function useViewInfo(args) {
|
export function useViewInfo(args) {
|
||||||
return useDbCore(args, 'views');
|
return useDbCore(args, ['views', 'matviews']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {import('dbgate-types').CollectionInfo} */
|
/** @returns {import('dbgate-types').CollectionInfo} */
|
||||||
@@ -344,7 +353,6 @@ export function useDatabaseServerVersion(args) {
|
|||||||
return useCore(databaseServerVersionLoader, args);
|
return useCore(databaseServerVersionLoader, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function getServerStatus() {
|
export function getServerStatus() {
|
||||||
return getCore(serverStatusLoader, {});
|
return getCore(serverStatusLoader, {});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,10 +21,10 @@
|
|||||||
$: objects = useDatabaseInfo({ conid, database });
|
$: objects = useDatabaseInfo({ conid, database });
|
||||||
$: status = useDatabaseStatus({ conid, database });
|
$: status = useDatabaseStatus({ conid, database });
|
||||||
|
|
||||||
// $: console.log('objects', $objects);
|
// $: console.log('OBJECTS', $objects);
|
||||||
|
|
||||||
$: objectList = _.flatten(
|
$: objectList = _.flatten(
|
||||||
['tables', 'collections', 'views', 'procedures', 'functions'].map(objectTypeField =>
|
['tables', 'collections', 'views', 'matviews', 'procedures', 'functions'].map(objectTypeField =>
|
||||||
_.sortBy(
|
_.sortBy(
|
||||||
(($objects || {})[objectTypeField] || []).map(obj => ({ ...obj, objectTypeField })),
|
(($objects || {})[objectTypeField] || []).map(obj => ({ ...obj, objectTypeField })),
|
||||||
['schemaName', 'pureName']
|
['schemaName', 'pureName']
|
||||||
@@ -35,6 +35,9 @@
|
|||||||
const handleRefreshDatabase = () => {
|
const handleRefreshDatabase = () => {
|
||||||
axiosInstance.post('database-connections/refresh', { conid, database });
|
axiosInstance.post('database-connections/refresh', { conid, database });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const OBJECT_TYPE_LABELS = { matviews: 'Materialized views' };
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $status && $status.name == 'error'}
|
{#if $status && $status.name == 'error'}
|
||||||
@@ -62,9 +65,10 @@
|
|||||||
<AppObjectList
|
<AppObjectList
|
||||||
list={objectList.map(x => ({ ...x, conid, database }))}
|
list={objectList.map(x => ({ ...x, conid, database }))}
|
||||||
module={databaseObjectAppObject}
|
module={databaseObjectAppObject}
|
||||||
groupFunc={data => _.startCase(data.objectTypeField)}
|
groupFunc={data => OBJECT_TYPE_LABELS[data.objectTypeField] || _.startCase(data.objectTypeField)}
|
||||||
subItemsComponent={SubColumnParamList}
|
subItemsComponent={SubColumnParamList}
|
||||||
isExpandable={data => data.objectTypeField == 'tables' || data.objectTypeField == 'views'}
|
isExpandable={data =>
|
||||||
|
data.objectTypeField == 'tables' || data.objectTypeField == 'views' || data.objectTypeField == 'matviews'}
|
||||||
expandIconFunc={chevronExpandIcon}
|
expandIconFunc={chevronExpandIcon}
|
||||||
{filter}
|
{filter}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -56,6 +56,12 @@ class Analyser extends DatabaseAnalyser {
|
|||||||
const pkColumns = await this.driver.query(this.pool, this.createQuery('primaryKeys', ['tables']));
|
const pkColumns = await this.driver.query(this.pool, this.createQuery('primaryKeys', ['tables']));
|
||||||
const fkColumns = await this.driver.query(this.pool, this.createQuery('foreignKeys', ['tables']));
|
const fkColumns = await this.driver.query(this.pool, this.createQuery('foreignKeys', ['tables']));
|
||||||
const views = await this.driver.query(this.pool, this.createQuery('views', ['views']));
|
const views = await this.driver.query(this.pool, this.createQuery('views', ['views']));
|
||||||
|
const matviews = this.driver.dialect.materializedViews
|
||||||
|
? await this.driver.query(this.pool, this.createQuery('matviews', ['matviews']))
|
||||||
|
: null;
|
||||||
|
const matviewColumns = this.driver.dialect.materializedViews
|
||||||
|
? await this.driver.query(this.pool, this.createQuery('matviewColumns', ['matviews']))
|
||||||
|
: null;
|
||||||
const routines = await this.driver.query(this.pool, this.createQuery('routines', ['procedures', 'functions']));
|
const routines = await this.driver.query(this.pool, this.createQuery('routines', ['procedures', 'functions']));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -108,6 +114,18 @@ class Analyser extends DatabaseAnalyser {
|
|||||||
.filter(col => col.pure_name == view.pure_name && col.schema_name == view.schema_name)
|
.filter(col => col.pure_name == view.pure_name && col.schema_name == view.schema_name)
|
||||||
.map(getColumnInfo),
|
.map(getColumnInfo),
|
||||||
})),
|
})),
|
||||||
|
matviews: matviews
|
||||||
|
? matviews.rows.map(matview => ({
|
||||||
|
objectId: `matviews:${matview.schema_name}.${matview.pure_name}`,
|
||||||
|
pureName: matview.pure_name,
|
||||||
|
schemaName: matview.schema_name,
|
||||||
|
contentHash: matview.hash_code,
|
||||||
|
createSql: `CREATE MATERIALIZED VIEW "${matview.schema_name}"."${matview.pure_name}"\nAS\n${matview.definition}`,
|
||||||
|
columns: matviewColumns.rows
|
||||||
|
.filter(col => col.pure_name == matview.pure_name && col.schema_name == matview.schema_name)
|
||||||
|
.map(getColumnInfo),
|
||||||
|
}))
|
||||||
|
: undefined,
|
||||||
procedures: routines.rows
|
procedures: routines.rows
|
||||||
.filter(x => x.object_type == 'PROCEDURE')
|
.filter(x => x.object_type == 'PROCEDURE')
|
||||||
.map(proc => ({
|
.map(proc => ({
|
||||||
@@ -133,6 +151,9 @@ class Analyser extends DatabaseAnalyser {
|
|||||||
? await this.driver.query(this.pool, this.createQuery('tableModifications'))
|
? await this.driver.query(this.pool, this.createQuery('tableModifications'))
|
||||||
: null;
|
: null;
|
||||||
const viewModificationsQueryData = await this.driver.query(this.pool, this.createQuery('viewModifications'));
|
const viewModificationsQueryData = await this.driver.query(this.pool, this.createQuery('viewModifications'));
|
||||||
|
const matviewModificationsQueryData = this.driver.dialect.materializedViews
|
||||||
|
? await this.driver.query(this.pool, this.createQuery('matviewModifications'))
|
||||||
|
: null;
|
||||||
const routineModificationsQueryData = await this.driver.query(this.pool, this.createQuery('routineModifications'));
|
const routineModificationsQueryData = await this.driver.query(this.pool, this.createQuery('routineModifications'));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -150,6 +171,14 @@ class Analyser extends DatabaseAnalyser {
|
|||||||
schemaName: x.schema_name,
|
schemaName: x.schema_name,
|
||||||
contentHash: x.hash_code,
|
contentHash: x.hash_code,
|
||||||
})),
|
})),
|
||||||
|
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
|
procedures: routineModificationsQueryData.rows
|
||||||
.filter(x => x.object_type == 'PROCEDURE')
|
.filter(x => x.object_type == 'PROCEDURE')
|
||||||
.map(x => ({
|
.map(x => ({
|
||||||
|
|||||||
@@ -2,11 +2,14 @@ const columns = require('./columns');
|
|||||||
const tableModifications = require('./tableModifications');
|
const tableModifications = require('./tableModifications');
|
||||||
const tableList = require('./tableList');
|
const tableList = require('./tableList');
|
||||||
const viewModifications = require('./viewModifications');
|
const viewModifications = require('./viewModifications');
|
||||||
|
const matviewModifications = require('./matviewModifications');
|
||||||
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 routines = require('./routines');
|
const routines = require('./routines');
|
||||||
const routineModifications = require('./routineModifications');
|
const routineModifications = require('./routineModifications');
|
||||||
|
const matviewColumns = require('./matviewColumns');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
columns,
|
columns,
|
||||||
@@ -18,4 +21,7 @@ module.exports = {
|
|||||||
views,
|
views,
|
||||||
routines,
|
routines,
|
||||||
routineModifications,
|
routineModifications,
|
||||||
|
matviews,
|
||||||
|
matviewModifications,
|
||||||
|
matviewColumns,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
module.exports = `
|
||||||
|
SELECT pg_namespace.nspname AS "schema_name"
|
||||||
|
, pg_class.relname AS "pure_name"
|
||||||
|
, pg_attribute.attname AS "column_name"
|
||||||
|
, pg_catalog.format_type(pg_attribute.atttypid, pg_attribute.atttypmod) AS "data_type"
|
||||||
|
FROM pg_catalog.pg_class
|
||||||
|
INNER JOIN pg_catalog.pg_namespace
|
||||||
|
ON pg_class.relnamespace = pg_namespace.oid
|
||||||
|
INNER JOIN pg_catalog.pg_attribute
|
||||||
|
ON pg_class.oid = pg_attribute.attrelid
|
||||||
|
-- Keeps only materialized views, and non-db/catalog/index columns
|
||||||
|
WHERE pg_class.relkind = 'm'
|
||||||
|
AND pg_attribute.attnum >= 1
|
||||||
|
AND ('matviews:' || pg_namespace.nspname || '.' || pg_class.relname) =OBJECT_ID_CONDITION
|
||||||
|
|
||||||
|
ORDER BY pg_attribute.attnum
|
||||||
|
`;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
module.exports = `
|
||||||
|
select
|
||||||
|
matviewname as "pure_name",
|
||||||
|
schemaname as "schema_name",
|
||||||
|
md5(definition) as "hash_code"
|
||||||
|
from
|
||||||
|
pg_catalog.pg_matviews WHERE schemaname NOT LIKE 'pg_%'
|
||||||
|
`;
|
||||||
10
plugins/dbgate-plugin-postgres/src/backend/sql/matviews.js
Normal file
10
plugins/dbgate-plugin-postgres/src/backend/sql/matviews.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
module.exports = `
|
||||||
|
select
|
||||||
|
matviewname as "pure_name",
|
||||||
|
schemaname as "schema_name",
|
||||||
|
definition as "definition",
|
||||||
|
md5(definition) as "hash_code"
|
||||||
|
from
|
||||||
|
pg_catalog.pg_matviews WHERE schemaname NOT LIKE 'pg_%'
|
||||||
|
and ('matviews:' || schemaname || '.' || matviewname) =OBJECT_ID_CONDITION
|
||||||
|
`;
|
||||||
@@ -29,6 +29,10 @@ const postgresDriver = {
|
|||||||
engine: 'postgres@dbgate-plugin-postgres',
|
engine: 'postgres@dbgate-plugin-postgres',
|
||||||
title: 'Postgre SQL',
|
title: 'Postgre SQL',
|
||||||
defaultPort: 5432,
|
defaultPort: 5432,
|
||||||
|
dialect: {
|
||||||
|
...dialect,
|
||||||
|
materializedViews: true,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @type {import('dbgate-types').EngineDriver} */
|
/** @type {import('dbgate-types').EngineDriver} */
|
||||||
|
|||||||
Reference in New Issue
Block a user