mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-18 04:26:01 +00:00
256 lines
7.8 KiB
TypeScript
256 lines
7.8 KiB
TypeScript
import { findForeignKeyForColumn } from 'dbgate-tools';
|
|
import type { DatabaseInfo, TableInfo, ViewInfo } from 'dbgate-types';
|
|
import { createPerspectiveNodeConfig, MultipleDatabaseInfo, PerspectiveConfig } from './PerspectiveConfig';
|
|
import { PerspectiveDataPattern, PerspectiveDataPatternDict } from './PerspectiveDataPattern';
|
|
import { PerspectiveTableNode } from './PerspectiveTreeNode';
|
|
|
|
const namePredicates = [
|
|
x => x.toLowerCase() == 'name',
|
|
x => x.toLowerCase() == 'title',
|
|
x => x.toLowerCase().includes('name'),
|
|
x => x.toLowerCase().includes('title'),
|
|
x => x.toLowerCase().includes('subject'),
|
|
];
|
|
|
|
function getPerspectiveDefaultColumns(
|
|
table: TableInfo | ViewInfo,
|
|
db: DatabaseInfo,
|
|
circularColumns?: string[]
|
|
): [string[], string[]] {
|
|
const columns = table.columns.map(x => x.columnName);
|
|
const predicates = [
|
|
...namePredicates,
|
|
x =>
|
|
table.columns
|
|
.find(y => y.columnName == x)
|
|
?.dataType?.toLowerCase()
|
|
?.includes('char'),
|
|
];
|
|
|
|
for (const predicate of predicates) {
|
|
const col = columns.find(predicate);
|
|
if (col) return [[col], null];
|
|
}
|
|
|
|
if (circularColumns) {
|
|
const keyPredicates = [
|
|
x => findForeignKeyForColumn(table as TableInfo, x)?.columns?.length == 1 && !circularColumns.includes(x),
|
|
x => findForeignKeyForColumn(table as TableInfo, x)?.columns?.length == 1,
|
|
];
|
|
|
|
for (const predicate of keyPredicates) {
|
|
const col = columns.find(predicate);
|
|
if (col) return [null, [col]];
|
|
}
|
|
}
|
|
|
|
return [[columns[0]], null];
|
|
}
|
|
|
|
function getPerspectiveDefaultCollectionColumns(pattern: PerspectiveDataPattern): string[] {
|
|
const columns = pattern.columns.map(x => x.name);
|
|
const predicates = [...namePredicates, x => pattern.columns.find(y => y.name == x)?.types?.includes('string')];
|
|
|
|
for (const predicate of predicates) {
|
|
const col = columns.find(predicate);
|
|
if (col) return [col];
|
|
}
|
|
}
|
|
|
|
export function perspectiveNodesHaveStructure(
|
|
config: PerspectiveConfig,
|
|
dbInfos: MultipleDatabaseInfo,
|
|
dataPatterns: PerspectiveDataPatternDict,
|
|
conid: string,
|
|
database: string
|
|
) {
|
|
for (const node of config.nodes) {
|
|
const db = dbInfos?.[node.conid || conid]?.[node.database || database];
|
|
if (!db) return false;
|
|
|
|
const table = db.tables.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName);
|
|
const view = db.views.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName);
|
|
const collection = db.collections.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName);
|
|
|
|
if (!table && !view && !collection) return false;
|
|
if (collection && !dataPatterns?.[node.designerId]) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
export function shouldProcessPerspectiveDefaultColunns(
|
|
config: PerspectiveConfig,
|
|
dbInfos: MultipleDatabaseInfo,
|
|
dataPatterns: PerspectiveDataPatternDict,
|
|
conid: string,
|
|
database: string
|
|
) {
|
|
const nodesNotProcessed = config.nodes.filter(x => !x.defaultColumnsProcessed);
|
|
if (nodesNotProcessed.length == 0) return false;
|
|
|
|
return perspectiveNodesHaveStructure(config, dbInfos, dataPatterns, conid, database);
|
|
}
|
|
|
|
function processPerspectiveDefaultColunnsStep(
|
|
config: PerspectiveConfig,
|
|
dbInfos: MultipleDatabaseInfo,
|
|
dataPatterns: PerspectiveDataPatternDict,
|
|
conid: string,
|
|
database: string
|
|
) {
|
|
const rootNode = config.nodes.find(x => x.designerId == config.rootDesignerId);
|
|
if (!rootNode) return null;
|
|
const rootDb = dbInfos?.[rootNode.conid || conid]?.[rootNode.database || database];
|
|
if (!rootDb) return null;
|
|
const rootTable = rootDb.tables.find(x => x.pureName == rootNode.pureName && x.schemaName == rootNode.schemaName);
|
|
const rootView = rootDb.views.find(x => x.pureName == rootNode.pureName && x.schemaName == rootNode.schemaName);
|
|
|
|
const root = new PerspectiveTableNode(
|
|
rootTable || rootView,
|
|
dbInfos,
|
|
config,
|
|
null,
|
|
null,
|
|
{ conid, database },
|
|
null,
|
|
config.rootDesignerId
|
|
);
|
|
|
|
for (const node of config.nodes) {
|
|
if (node.defaultColumnsProcessed) continue;
|
|
|
|
const db = dbInfos?.[node.conid || conid]?.[node.database || database];
|
|
if (!db) continue;
|
|
|
|
const table = db.tables.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName);
|
|
const view = db.views.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName);
|
|
const collection = db.collections.find(x => x.pureName == node.pureName && x.schemaName == node.schemaName);
|
|
|
|
if (table || view) {
|
|
const treeNode = root.findNodeByDesignerId(node.designerId);
|
|
|
|
if (!treeNode) {
|
|
const [defaultColumns] = getPerspectiveDefaultColumns(table || view, db, null);
|
|
|
|
return {
|
|
...config,
|
|
nodes: config.nodes.map(n =>
|
|
n.designerId == node.designerId
|
|
? {
|
|
...n,
|
|
defaultColumnsProcessed: true,
|
|
checkedColumns: defaultColumns,
|
|
}
|
|
: n
|
|
),
|
|
};
|
|
}
|
|
|
|
const circularColumns = treeNode.childNodes.filter(x => x.isCircular).map(x => x.columnName);
|
|
const [defaultColumns, defaultRefs] = getPerspectiveDefaultColumns(table || view, db, circularColumns);
|
|
|
|
if (defaultRefs) {
|
|
const childNode = treeNode.childNodes.find(x => x.columnName == defaultRefs[0]);
|
|
if (childNode?.designerId) {
|
|
return {
|
|
...config,
|
|
nodes: config.nodes.map(n =>
|
|
n.designerId == childNode.designerId
|
|
? {
|
|
...n,
|
|
isNodeChecked: true,
|
|
}
|
|
: n.designerId == node.designerId
|
|
? {
|
|
...n,
|
|
defaultColumnsProcessed: true,
|
|
}
|
|
: n
|
|
),
|
|
};
|
|
} else if (childNode) {
|
|
const [newConfig, nodeConfig] = childNode.ensureNodeConfig(config);
|
|
nodeConfig.isNodeChecked = true;
|
|
|
|
return {
|
|
...newConfig,
|
|
nodes: newConfig.nodes.map(n =>
|
|
n.designerId == node.designerId
|
|
? {
|
|
...n,
|
|
defaultColumnsProcessed: true,
|
|
}
|
|
: n
|
|
),
|
|
};
|
|
}
|
|
} else {
|
|
return {
|
|
...config,
|
|
nodes: config.nodes.map(n =>
|
|
n.designerId == node.designerId
|
|
? {
|
|
...n,
|
|
defaultColumnsProcessed: true,
|
|
checkedColumns: defaultColumns,
|
|
}
|
|
: n
|
|
),
|
|
};
|
|
}
|
|
}
|
|
|
|
if (collection) {
|
|
const defaultColumns = getPerspectiveDefaultCollectionColumns(dataPatterns?.[node.designerId]);
|
|
return {
|
|
...config,
|
|
nodes: config.nodes.map(n =>
|
|
n.designerId == node.designerId
|
|
? {
|
|
...n,
|
|
defaultColumnsProcessed: true,
|
|
checkedColumns: defaultColumns,
|
|
}
|
|
: n
|
|
),
|
|
};
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function markAllProcessed(config: PerspectiveConfig): PerspectiveConfig {
|
|
return {
|
|
...config,
|
|
nodes: config.nodes.map(x => ({
|
|
...x,
|
|
defaultColumnsProcessed: true,
|
|
})),
|
|
};
|
|
}
|
|
|
|
export function processPerspectiveDefaultColunns(
|
|
config: PerspectiveConfig,
|
|
dbInfos: MultipleDatabaseInfo,
|
|
dataPatterns: PerspectiveDataPatternDict,
|
|
conid: string,
|
|
database: string
|
|
): PerspectiveConfig {
|
|
while (config.nodes.filter(x => !x.defaultColumnsProcessed).length > 0) {
|
|
const newConfig = processPerspectiveDefaultColunnsStep(config, dbInfos, dataPatterns, conid, database);
|
|
if (!newConfig) {
|
|
return markAllProcessed(config);
|
|
}
|
|
if (
|
|
newConfig.nodes.filter(x => x.defaultColumnsProcessed).length <=
|
|
config.nodes.filter(x => x.defaultColumnsProcessed).length
|
|
) {
|
|
return markAllProcessed(config);
|
|
}
|
|
config = newConfig;
|
|
}
|
|
return markAllProcessed(config);
|
|
}
|