perspectives: custom join over different databases

This commit is contained in:
Jan Prochazka
2022-08-06 16:44:37 +02:00
parent aff1fe0b3d
commit 86d7d61cc5
8 changed files with 105 additions and 40 deletions

View File

@@ -1,4 +1,4 @@
import { ForeignKeyInfo } from "dbgate-types"; import { DatabaseInfo, ForeignKeyInfo } from 'dbgate-types';
export interface PerspectiveConfigColumns { export interface PerspectiveConfigColumns {
expandedColumns: string[]; expandedColumns: string[];
@@ -55,3 +55,27 @@ export type ChangePerspectiveConfigFunc = (
changeFunc: (config: PerspectiveConfig) => PerspectiveConfig, changeFunc: (config: PerspectiveConfig) => PerspectiveConfig,
reload?: boolean reload?: boolean
) => void; ) => void;
export function extractPerspectiveDatabases(
{ conid, database },
cfg: PerspectiveConfig
): { conid: string; database: string }[] {
const res: { conid: string; database: string }[] = [];
res.push({ conid, database });
function add(conid, database) {
if (res.find(x => x.conid == conid && x.database == database)) return;
res.push({ conid, database });
}
for (const custom of cfg.customJoins) {
add(custom.conid || conid, custom.database || database);
}
return res;
}
export interface MultipleDatabaseInfo {
[conid: string]: {
[database: string]: DatabaseInfo;
};
}

View File

@@ -1,7 +1,7 @@
import { ColumnInfo, DatabaseInfo, ForeignKeyInfo, RangeDefinition, TableInfo, ViewInfo } from 'dbgate-types'; import { ColumnInfo, DatabaseInfo, ForeignKeyInfo, RangeDefinition, TableInfo, ViewInfo } from 'dbgate-types';
import { clearConfigCache } from 'prettier';
import { import {
ChangePerspectiveConfigFunc, ChangePerspectiveConfigFunc,
MultipleDatabaseInfo,
PerspectiveConfig, PerspectiveConfig,
PerspectiveConfigColumns, PerspectiveConfigColumns,
PerspectiveCustomJoinConfig, PerspectiveCustomJoinConfig,
@@ -54,6 +54,7 @@ export interface PerspectiveDataLoadPropsWithNode {
export abstract class PerspectiveTreeNode { export abstract class PerspectiveTreeNode {
constructor( constructor(
public dbs: MultipleDatabaseInfo,
public config: PerspectiveConfig, public config: PerspectiveConfig,
public setConfig: ChangePerspectiveConfigFunc, public setConfig: ChangePerspectiveConfigFunc,
public parentNode: PerspectiveTreeNode, public parentNode: PerspectiveTreeNode,
@@ -118,6 +119,9 @@ export abstract class PerspectiveTreeNode {
get customJoinConfig(): PerspectiveCustomJoinConfig { get customJoinConfig(): PerspectiveCustomJoinConfig {
return null; return null;
} }
get db(): DatabaseInfo {
return this.dbs?.[this.databaseConfig.conid]?.[this.databaseConfig.database];
}
getChildMatchColumns() { getChildMatchColumns() {
return []; return [];
@@ -256,20 +260,20 @@ export class PerspectiveTableColumnNode extends PerspectiveTreeNode {
constructor( constructor(
public column: ColumnInfo, public column: ColumnInfo,
public table: TableInfo | ViewInfo, public table: TableInfo | ViewInfo,
public db: DatabaseInfo, dbs: MultipleDatabaseInfo,
config: PerspectiveConfig, config: PerspectiveConfig,
setConfig: ChangePerspectiveConfigFunc, setConfig: ChangePerspectiveConfigFunc,
dataProvider: PerspectiveDataProvider, dataProvider: PerspectiveDataProvider,
databaseConfig: PerspectiveDatabaseConfig, databaseConfig: PerspectiveDatabaseConfig,
parentNode: PerspectiveTreeNode parentNode: PerspectiveTreeNode
) { ) {
super(config, setConfig, parentNode, dataProvider, databaseConfig); super(dbs, config, setConfig, parentNode, dataProvider, databaseConfig);
this.foreignKey = (table as TableInfo)?.foreignKeys?.find( this.foreignKey = (table as TableInfo)?.foreignKeys?.find(
fk => fk.columns.length == 1 && fk.columns[0].columnName == column.columnName fk => fk.columns.length == 1 && fk.columns[0].columnName == column.columnName
); );
this.refTable = db.tables.find( this.refTable = this.db.tables.find(
x => x.pureName == this.foreignKey?.refTableName && x.schemaName == this.foreignKey?.refSchemaName x => x.pureName == this.foreignKey?.refTableName && x.schemaName == this.foreignKey?.refSchemaName
); );
} }
@@ -349,7 +353,7 @@ export class PerspectiveTableColumnNode extends PerspectiveTreeNode {
return getTableChildPerspectiveNodes( return getTableChildPerspectiveNodes(
tbl, tbl,
this.db, this.dbs,
this.config, this.config,
this.setConfig, this.setConfig,
this.dataProvider, this.dataProvider,
@@ -412,14 +416,14 @@ export class PerspectiveTableColumnNode extends PerspectiveTreeNode {
export class PerspectiveTableNode extends PerspectiveTreeNode { export class PerspectiveTableNode extends PerspectiveTreeNode {
constructor( constructor(
public table: TableInfo, public table: TableInfo,
public db: DatabaseInfo, dbs: MultipleDatabaseInfo,
config: PerspectiveConfig, config: PerspectiveConfig,
setConfig: ChangePerspectiveConfigFunc, setConfig: ChangePerspectiveConfigFunc,
public dataProvider: PerspectiveDataProvider, public dataProvider: PerspectiveDataProvider,
databaseConfig: PerspectiveDatabaseConfig, databaseConfig: PerspectiveDatabaseConfig,
parentNode: PerspectiveTreeNode parentNode: PerspectiveTreeNode
) { ) {
super(config, setConfig, parentNode, dataProvider, databaseConfig); super(dbs, config, setConfig, parentNode, dataProvider, databaseConfig);
} }
getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps { getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps {
@@ -448,7 +452,7 @@ export class PerspectiveTableNode extends PerspectiveTreeNode {
get childNodes(): PerspectiveTreeNode[] { get childNodes(): PerspectiveTreeNode[] {
return getTableChildPerspectiveNodes( return getTableChildPerspectiveNodes(
this.table, this.table,
this.db, this.dbs,
this.config, this.config,
this.setConfig, this.setConfig,
this.dataProvider, this.dataProvider,
@@ -482,14 +486,14 @@ export class PerspectiveTableNode extends PerspectiveTreeNode {
export class PerspectiveViewNode extends PerspectiveTreeNode { export class PerspectiveViewNode extends PerspectiveTreeNode {
constructor( constructor(
public view: ViewInfo, public view: ViewInfo,
public db: DatabaseInfo, dbs: MultipleDatabaseInfo,
config: PerspectiveConfig, config: PerspectiveConfig,
setConfig: ChangePerspectiveConfigFunc, setConfig: ChangePerspectiveConfigFunc,
public dataProvider: PerspectiveDataProvider, public dataProvider: PerspectiveDataProvider,
databaseConfig: PerspectiveDatabaseConfig, databaseConfig: PerspectiveDatabaseConfig,
parentNode: PerspectiveTreeNode parentNode: PerspectiveTreeNode
) { ) {
super(config, setConfig, parentNode, dataProvider, databaseConfig); super(dbs, config, setConfig, parentNode, dataProvider, databaseConfig);
} }
getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps { getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps {
@@ -518,7 +522,7 @@ export class PerspectiveViewNode extends PerspectiveTreeNode {
get childNodes(): PerspectiveTreeNode[] { get childNodes(): PerspectiveTreeNode[] {
return getTableChildPerspectiveNodes( return getTableChildPerspectiveNodes(
this.view, this.view,
this.db, this.dbs,
this.config, this.config,
this.setConfig, this.setConfig,
this.dataProvider, this.dataProvider,
@@ -540,7 +544,7 @@ export class PerspectiveTableReferenceNode extends PerspectiveTableNode {
constructor( constructor(
public foreignKey: ForeignKeyInfo, public foreignKey: ForeignKeyInfo,
table: TableInfo, table: TableInfo,
db: DatabaseInfo, dbs: MultipleDatabaseInfo,
config: PerspectiveConfig, config: PerspectiveConfig,
setConfig: ChangePerspectiveConfigFunc, setConfig: ChangePerspectiveConfigFunc,
public dataProvider: PerspectiveDataProvider, public dataProvider: PerspectiveDataProvider,
@@ -548,7 +552,7 @@ export class PerspectiveTableReferenceNode extends PerspectiveTableNode {
public isMultiple: boolean, public isMultiple: boolean,
parentNode: PerspectiveTreeNode parentNode: PerspectiveTreeNode
) { ) {
super(table, db, config, setConfig, dataProvider, databaseConfig, parentNode); super(table, dbs, config, setConfig, dataProvider, databaseConfig, parentNode);
} }
matchChildRow(parentRow: any, childRow: any): boolean { matchChildRow(parentRow: any, childRow: any): boolean {
@@ -605,22 +609,15 @@ export class PerspectiveTableReferenceNode extends PerspectiveTableNode {
export class PerspectiveCustomJoinTreeNode extends PerspectiveTableNode { export class PerspectiveCustomJoinTreeNode extends PerspectiveTableNode {
constructor( constructor(
public customJoin: PerspectiveCustomJoinConfig, public customJoin: PerspectiveCustomJoinConfig,
db: DatabaseInfo, table: TableInfo,
dbs: MultipleDatabaseInfo,
config: PerspectiveConfig, config: PerspectiveConfig,
setConfig: ChangePerspectiveConfigFunc, setConfig: ChangePerspectiveConfigFunc,
public dataProvider: PerspectiveDataProvider, public dataProvider: PerspectiveDataProvider,
databaseConfig: PerspectiveDatabaseConfig, databaseConfig: PerspectiveDatabaseConfig,
parentNode: PerspectiveTreeNode parentNode: PerspectiveTreeNode
) { ) {
super( super(table, dbs, config, setConfig, dataProvider, databaseConfig, parentNode);
db.tables.find(x => x.pureName == customJoin.refTableName && x.schemaName == customJoin.refSchemaName),
db,
config,
setConfig,
dataProvider,
databaseConfig,
parentNode
);
} }
matchChildRow(parentRow: any, childRow: any): boolean { matchChildRow(parentRow: any, childRow: any): boolean {
@@ -677,7 +674,7 @@ export class PerspectiveCustomJoinTreeNode extends PerspectiveTableNode {
export function getTableChildPerspectiveNodes( export function getTableChildPerspectiveNodes(
table: TableInfo | ViewInfo, table: TableInfo | ViewInfo,
db: DatabaseInfo, dbs: MultipleDatabaseInfo,
config: PerspectiveConfig, config: PerspectiveConfig,
setConfig: ChangePerspectiveConfigFunc, setConfig: ChangePerspectiveConfigFunc,
dataProvider: PerspectiveDataProvider, dataProvider: PerspectiveDataProvider,
@@ -685,9 +682,11 @@ export function getTableChildPerspectiveNodes(
parentColumn: PerspectiveTreeNode parentColumn: PerspectiveTreeNode
) { ) {
if (!table) return []; if (!table) return [];
const db = parentColumn.db;
const columnNodes = table.columns.map( const columnNodes = table.columns.map(
col => new PerspectiveTableColumnNode(col, table, db, config, setConfig, dataProvider, databaseConfig, parentColumn) col =>
new PerspectiveTableColumnNode(col, table, dbs, config, setConfig, dataProvider, databaseConfig, parentColumn)
); );
const circularColumns = columnNodes.filter(x => x.isCircular).map(x => x.columnName); const circularColumns = columnNodes.filter(x => x.isCircular).map(x => x.columnName);
const defaultColumns = getPerspectiveDefaultColumns(table, db, circularColumns); const defaultColumns = getPerspectiveDefaultColumns(table, db, circularColumns);
@@ -709,7 +708,7 @@ export function getTableChildPerspectiveNodes(
new PerspectiveTableReferenceNode( new PerspectiveTableReferenceNode(
fk, fk,
tbl, tbl,
db, dbs,
config, config,
setConfig, setConfig,
dataProvider, dataProvider,
@@ -726,9 +725,17 @@ export function getTableChildPerspectiveNodes(
const customs = []; const customs = [];
for (const join of config.customJoins || []) { for (const join of config.customJoins || []) {
if (join.baseUniqueName == parentColumn.uniqueName) { if (join.baseUniqueName == parentColumn.uniqueName) {
customs.push( const newConfig = { ...databaseConfig };
new PerspectiveCustomJoinTreeNode(join, db, config, setConfig, dataProvider, databaseConfig, parentColumn) if (join.conid) newConfig.conid = join.conid;
); if (join.database) newConfig.database = join.database;
const db = dbs?.[newConfig.conid]?.[newConfig.database];
const table = db?.tables?.find(x => x.pureName == join.refTableName && x.schemaName == join.refSchemaName);
if (table) {
customs.push(
new PerspectiveCustomJoinTreeNode(join, table, dbs, config, setConfig, dataProvider, newConfig, parentColumn)
);
}
} }
} }
res.push(..._sortBy(customs, 'title')); res.push(..._sortBy(customs, 'title'));

View File

@@ -17,3 +17,4 @@ export * from './deleteCascade';
export * from './PerspectiveDisplay'; export * from './PerspectiveDisplay';
export * from './PerspectiveDataProvider'; export * from './PerspectiveDataProvider';
export * from './PerspectiveCache'; export * from './PerspectiveCache';
export * from './PerspectiveConfig';

View File

@@ -9,7 +9,15 @@ import artistDataAlbumTrack from './artistDataAlbumTrack';
test('test flat view', () => { test('test flat view', () => {
const artistTable = chinookDbInfo.tables.find(x => x.pureName == 'Artist'); const artistTable = chinookDbInfo.tables.find(x => x.pureName == 'Artist');
const root = new PerspectiveTableNode(artistTable, chinookDbInfo, createPerspectiveConfig(), null, null, null, null); const root = new PerspectiveTableNode(
artistTable,
{ conid: { db: chinookDbInfo } },
createPerspectiveConfig(),
null,
null,
{ conid: 'conid', database: 'db' },
null
);
const display = new PerspectiveDisplay(root, artistDataFlat); const display = new PerspectiveDisplay(root, artistDataFlat);
// console.log(display.loadIndicatorsCounts); // console.log(display.loadIndicatorsCounts);
@@ -29,11 +37,11 @@ test('test one level nesting', () => {
const artistTable = chinookDbInfo.tables.find(x => x.pureName == 'Artist'); const artistTable = chinookDbInfo.tables.find(x => x.pureName == 'Artist');
const root = new PerspectiveTableNode( const root = new PerspectiveTableNode(
artistTable, artistTable,
chinookDbInfo, { conid: { db: chinookDbInfo } },
{ ...createPerspectiveConfig(), checkedColumns: ['Artist.Album'] }, { ...createPerspectiveConfig(), checkedColumns: ['Artist.Album'] },
null, null,
null, null,
null, { conid: 'conid', database: 'db' },
null null
); );
const display = new PerspectiveDisplay(root, artistDataAlbum); const display = new PerspectiveDisplay(root, artistDataAlbum);
@@ -79,11 +87,11 @@ test('test two level nesting', () => {
const artistTable = chinookDbInfo.tables.find(x => x.pureName == 'Artist'); const artistTable = chinookDbInfo.tables.find(x => x.pureName == 'Artist');
const root = new PerspectiveTableNode( const root = new PerspectiveTableNode(
artistTable, artistTable,
chinookDbInfo, { conid: { db: chinookDbInfo } },
{ ...createPerspectiveConfig(), checkedColumns: ['Artist.Album', 'Artist.Album.Track'] }, { ...createPerspectiveConfig(), checkedColumns: ['Artist.Album', 'Artist.Album.Track'] },
null, null,
null, null,
null, { conid: 'conid', database: 'db' },
null null
); );
const display = new PerspectiveDisplay(root, artistDataAlbumTrack); const display = new PerspectiveDisplay(root, artistDataAlbumTrack);

View File

@@ -316,6 +316,8 @@
refTableName, refTableName,
refSchemaName, refSchemaName,
columns, columns,
conid: conidOverride,
database: databaseOverride,
}; };
setConfig(cfg => ({ setConfig(cfg => ({
...cfg, ...cfg,

View File

@@ -180,6 +180,8 @@
const pureName = td.getAttribute('data-pureName'); const pureName = td.getAttribute('data-pureName');
const schemaName = td.getAttribute('data-schemaName'); const schemaName = td.getAttribute('data-schemaName');
const dataConid = td.getAttribute('data-conid');
const dataDatabase = td.getAttribute('data-database');
if (pureName) { if (pureName) {
res.push({ res.push({
text: `Open table ${pureName}`, text: `Open table ${pureName}`,
@@ -191,8 +193,8 @@
props: { props: {
schemaName, schemaName,
pureName, pureName,
conid, conid: dataConid || conid,
database, database: dataDatabase || database,
objectTypeField: 'tables', objectTypeField: 'tables',
}, },
}); });

View File

@@ -17,6 +17,7 @@
import { import {
ChangeConfigFunc, ChangeConfigFunc,
ChangePerspectiveConfigFunc, ChangePerspectiveConfigFunc,
extractPerspectiveDatabases,
getTableChildPerspectiveNodes, getTableChildPerspectiveNodes,
GridConfig, GridConfig,
PerspectiveConfig, PerspectiveConfig,
@@ -54,6 +55,7 @@
import SearchBoxWrapper from '../elements/SearchBoxWrapper.svelte'; import SearchBoxWrapper from '../elements/SearchBoxWrapper.svelte';
import SearchInput from '../elements/SearchInput.svelte'; import SearchInput from '../elements/SearchInput.svelte';
import CloseSearchButton from '../buttons/CloseSearchButton.svelte'; import CloseSearchButton from '../buttons/CloseSearchButton.svelte';
import { useMultipleDatabaseInfo } from '../utility/useMultipleDatabaseInfo';
const dbg = debug('dbgate:PerspectiveView'); const dbg = debug('dbgate:PerspectiveView');
@@ -95,16 +97,16 @@
}); });
} }
const dbInfo = useDatabaseInfo({ conid, database }); const dbInfos = useMultipleDatabaseInfo(extractPerspectiveDatabases({ conid, database }, config));
const tableInfo = useTableInfo({ conid, database, schemaName, pureName }); const tableInfo = useTableInfo({ conid, database, schemaName, pureName });
const viewInfo = useViewInfo({ conid, database, schemaName, pureName }); const viewInfo = useViewInfo({ conid, database, schemaName, pureName });
$: dataProvider = new PerspectiveDataProvider(cache, loader); $: dataProvider = new PerspectiveDataProvider(cache, loader);
$: loader = new PerspectiveDataLoader(apiCall); $: loader = new PerspectiveDataLoader(apiCall);
$: root = $tableInfo $: root = $tableInfo
? new PerspectiveTableNode($tableInfo, $dbInfo, config, setConfig, dataProvider, { conid, database }, null) ? new PerspectiveTableNode($tableInfo, $dbInfos, config, setConfig, dataProvider, { conid, database }, null)
: $viewInfo : $viewInfo
? new PerspectiveViewNode($viewInfo, $dbInfo, config, setConfig, dataProvider, { conid, database }, null) ? new PerspectiveViewNode($viewInfo, $dbInfos, config, setConfig, dataProvider, { conid, database }, null)
: null; : null;
// $: console.log('CONFIG', config); // $: console.log('CONFIG', config);

View File

@@ -0,0 +1,19 @@
import { Readable, writable } from 'svelte/store';
import { getDatabaseInfo } from './metadataLoaders';
import { MultipleDatabaseInfo } from 'dbgate-datalib';
export function useMultipleDatabaseInfo(dbs: { conid: string; database: string }[]): Readable<MultipleDatabaseInfo> {
const res = writable({});
for (const { conid, database } of dbs) {
getDatabaseInfo({ conid, database }).then(dbInfo => {
res.update(old => ({
...old,
[conid]: {
...old[conid],
[database]: dbInfo,
},
}));
});
}
return res;
}