This commit is contained in:
Jan Prochazka 2020-03-08 20:52:26 +01:00
parent 8d731731b3
commit caf5453975
4 changed files with 125 additions and 53 deletions

View File

@ -16,6 +16,15 @@ export interface DisplayColumn {
isPrimaryKey: boolean;
foreignKey: ForeignKeyInfo;
isChecked?: boolean;
hintColumnName?: string;
}
export type ReferenceActionResult = 'noAction' | 'loadRequired' | 'refAdded';
export function combineReferenceActions(a: ReferenceActionResult, b: ReferenceActionResult): ReferenceActionResult {
if (a == 'loadRequired' || b == 'loadRequired') return 'loadRequired';
if (a == 'refAdded' || b == 'refAdded') return 'refAdded';
return 'noAction';
}
export abstract class GridDisplay {
@ -81,31 +90,35 @@ export abstract class GridDisplay {
const res = [];
for (const item of list) {
res.push(item);
if (this.isExpandedColumn(item.uniqueName)) res.push(...this.getExpandedColumns(item, item.uniqueName));
if (this.isExpandedColumn(item.uniqueName)) res.push(...this.getExpandedColumns(item));
}
return res;
}
getExpandedColumns(column: DisplayColumn, uniqueName: string) {
const table = this.cache.tables[uniqueName];
getExpandedColumns(column: DisplayColumn) {
const table = this.cache.tables[column.uniqueName];
if (table) {
return this.enrichExpandedColumns(this.getDisplayColumns(table, column.uniquePath));
} else {
// load expanded columns
const { foreignKey } = column;
this.getTableInfo({ schemaName: foreignKey.refSchemaName, pureName: foreignKey.refTableName }).then(table => {
this.setCache({
...this.cache,
tables: {
...this.cache.tables,
[uniqueName]: table,
},
});
});
this.requireFkTarget(column);
}
return [];
}
requireFkTarget(column: DisplayColumn) {
const { uniqueName, foreignKey } = column;
this.getTableInfo({ schemaName: foreignKey.refSchemaName, pureName: foreignKey.refTableName }).then(table => {
this.setCache({
...this.cache,
tables: {
...this.cache.tables,
[uniqueName]: table,
},
});
});
}
isColumnChecked(column: DisplayColumn) {
return column.uniquePath.length == 1
? !this.config.hiddenColumns.includes(column.uniqueName)
@ -130,8 +143,8 @@ export abstract class GridDisplay {
};
}
addAddedColumnsToSelect(select: Select, columns: DisplayColumn[], parentAlias: string) {
let res = false;
addAddedColumnsToSelect(select: Select, columns: DisplayColumn[], parentAlias: string): ReferenceActionResult {
let res: ReferenceActionResult = 'noAction';
for (const column of columns) {
if (this.config.addedColumns.includes(column.uniqueName)) {
select.columns.push({
@ -139,52 +152,35 @@ export abstract class GridDisplay {
columnName: column.columnName,
source: { name: column, alias: parentAlias },
});
res = true;
res = 'refAdded';
}
}
return res;
}
addJoinsFromExpandedColumns(select: Select, columns: DisplayColumn[], parentAlias: string) {
let res = false;
addJoinsFromExpandedColumns(select: Select, columns: DisplayColumn[], parentAlias: string): ReferenceActionResult {
let res: ReferenceActionResult = 'noAction';
for (const column of columns) {
if (this.isExpandedColumn(column.uniqueName)) {
const table = this.cache.tables[column.uniqueName];
if (table) {
const childAlias = `${column.uniqueName}_ref`;
const subcolumns = this.getDisplayColumns(table, column.uniquePath);
const usedTable =
this.addJoinsFromExpandedColumns(select, subcolumns, childAlias) ||
this.addAddedColumnsToSelect(select, subcolumns, childAlias);
const tableAction = combineReferenceActions(
this.addJoinsFromExpandedColumns(select, subcolumns, childAlias),
this.addAddedColumnsToSelect(select, subcolumns, childAlias)
);
if (usedTable) {
select.from.relations = [
...(select.from.relations || []),
{
joinType: 'LEFT JOIN',
name: table,
alias: childAlias,
conditions: [
{
conditionType: 'binary',
operator: '=',
left: {
exprType: 'column',
columnName: column.columnName,
source: { name: column, alias: parentAlias },
},
right: {
exprType: 'column',
columnName: table.primaryKey.columns[0].columnName,
source: { name: table, alias: childAlias },
},
},
],
},
];
res = true;
if (tableAction == 'refAdded') {
this.addReferenceToSelect(select, parentAlias, column);
res = 'refAdded';
}
if (tableAction == 'loadRequired') {
return 'loadRequired';
}
} else {
this.requireFkTarget(column);
res = 'loadRequired';
}
}
}
@ -192,10 +188,72 @@ export abstract class GridDisplay {
// const addedColumns = this.getGridColumns().filter(x=>x.)
}
addReferenceToSelect(select: Select, parentAlias: string, column: DisplayColumn) {
const childAlias = `${column.uniqueName}_ref`;
if ((select.from.relations || []).find(x => x.alias == childAlias)) return;
const table = this.cache.tables[column.uniqueName];
select.from.relations = [
...(select.from.relations || []),
{
joinType: 'LEFT JOIN',
name: table,
alias: childAlias,
conditions: [
{
conditionType: 'binary',
operator: '=',
left: {
exprType: 'column',
columnName: column.columnName,
source: { name: column, alias: parentAlias },
},
right: {
exprType: 'column',
columnName: table.primaryKey.columns[0].columnName,
source: { name: table, alias: childAlias },
},
},
],
},
];
}
addHintsToSelect(select: Select): ReferenceActionResult {
let res: ReferenceActionResult = 'noAction';
for (const column of this.getGridColumns()) {
if (column.foreignKey) {
const table = this.cache.tables[column.uniqueName];
if (table) {
const hintColumn = table.columns.find(x => x?.dataType?.toLowerCase()?.includes('char'));
if (hintColumn) {
const parentUniqueName = column.uniquePath.slice(0, -1).join('.');
this.addReferenceToSelect(select, parentUniqueName ? `${parentUniqueName}_ref` : 'basetbl', column);
const childAlias = `${column.uniqueName}_ref`;
select.columns.push({
exprType: 'column',
columnName: hintColumn.columnName,
alias: `hint_${column.columnName}`,
source: { alias: childAlias },
});
res = 'refAdded';
}
} else {
this.requireFkTarget(column);
res = 'loadRequired';
}
}
}
return res;
}
getDisplayColumns(table: TableInfo, parentPath: string[]) {
return table?.columns
?.map(col => this.getDisplayColumn(table, col, parentPath))
?.map(col => ({ ...col, isChecked: this.isColumnChecked(col) }));
?.map(col => ({
...col,
isChecked: this.isColumnChecked(col),
hintColumnName: col.foreignKey ? `hint_${col.columnName}` : null,
}));
}
getColumns(columnFilter) {

View File

@ -1,4 +1,4 @@
import { GridDisplay } from './GridDisplay';
import { GridDisplay, combineReferenceActions } from './GridDisplay';
import { Select, treeToSql, dumpSqlSelect } from '@dbgate/sqltree';
import { TableInfo, EngineDriver } from '@dbgate/types';
import { GridConfig, GridCache } from './GridConfig';
@ -36,12 +36,19 @@ export class TableGridDisplay extends GridDisplay {
},
],
};
this.addJoinsFromExpandedColumns(select, this.columns, 'basetbl');
const action = combineReferenceActions(
this.addJoinsFromExpandedColumns(select, this.columns, 'basetbl'),
this.addHintsToSelect(select)
);
if (action == 'loadRequired') {
return null;
}
return select;
}
getPageQuery(offset: number, count: number) {
const select = this.createSelect();
if (!select) return null;
if (this.driver.dialect.rangeSelect) select.range = { offset: offset, limit: count };
else if (this.driver.dialect.limitSelect) select.topRecords = count;
const sql = treeToSql(this.driver, select, dumpSqlSelect);

View File

@ -2,7 +2,7 @@
import React from 'react';
import styled from 'styled-components';
import { SequenceIcon } from '../icons';
import { SequenceIcon, ForeignKeyIcon } from '../icons';
const Label = styled.span`
font-weight: ${props => (props.notNull ? 'bold' : 'normal')};
@ -12,6 +12,7 @@ const Label = styled.span`
export default function ColumnLabel(column) {
let Icon = null;
if (column.autoIncrement) Icon = SequenceIcon;
if (column.foreignKey) Icon = ForeignKeyIcon;
return (
<Label {...column}>
{Icon ? <Icon /> : null} {column.headerText || column.columnName}

View File

@ -64,6 +64,10 @@ const TableBodyCell = styled.td`
white-space: nowrap;
overflow: hidden;
`;
const HintSpan = styled.span`
color: gray;
margin-left: 5px;
`;
/** @param props {import('./types').DataGridProps} */
export default function DataGridCore(props) {
@ -163,7 +167,8 @@ export default function DataGridCore(props) {
React.useEffect(() => {
if (!isLoadedAll && firstVisibleRowScrollIndex + visibleRowCountUpperBound >= loadedRows.length) {
loadNextData();
const sql = display.getPageQuery(0, 1);
if (sql) loadNextData();
}
if (display.cache.refreshTime > loadedTime) {
reload();
@ -314,6 +319,7 @@ export default function DataGridCore(props) {
style={{ width: col.widthPx, minWidth: col.widthPx, maxWidth: col.widthPx }}
>
{row[col.columnName]}
{col.hintColumnName && <HintSpan>{row[col.hintColumnName]}</HintSpan>}
</TableBodyCell>
))}
</TableBodyRow>