diff --git a/packages/datalib/src/GridDisplay.ts b/packages/datalib/src/GridDisplay.ts index c3aba307e..05580b5f0 100644 --- a/packages/datalib/src/GridDisplay.ts +++ b/packages/datalib/src/GridDisplay.ts @@ -29,7 +29,7 @@ export interface DisplayColumn { foreignKey?: ForeignKeyInfo; isExpandable?: boolean; isChecked?: boolean; - hintColumnName?: string; + hintColumnNames?: string[]; dataType?: string; filterType?: boolean; isStructured?: boolean; diff --git a/packages/datalib/src/TableFormViewDisplay.ts b/packages/datalib/src/TableFormViewDisplay.ts index 7c6bb292f..1c568c349 100644 --- a/packages/datalib/src/TableFormViewDisplay.ts +++ b/packages/datalib/src/TableFormViewDisplay.ts @@ -15,6 +15,7 @@ import { import { TableGridDisplay } from './TableGridDisplay'; import stableStringify from 'json-stable-stringify'; import { ChangeSetFieldDefinition, ChangeSetRowDefinition } from './ChangeSet'; +import { DictionaryDescriptionFunc } from '.'; export class TableFormViewDisplay extends FormViewDisplay { // use utility functions from GridDisplay and publish result in FromViewDisplay interface @@ -29,7 +30,8 @@ export class TableFormViewDisplay extends FormViewDisplay { setCache: ChangeCacheFunc, dbinfo: DatabaseInfo, displayOptions, - serverVersion + serverVersion, + getDictionaryDescription: DictionaryDescriptionFunc = null ) { super(config, setConfig, cache, setCache, driver, dbinfo, serverVersion); this.gridDisplay = new TableGridDisplay( @@ -41,7 +43,8 @@ export class TableFormViewDisplay extends FormViewDisplay { setCache, dbinfo, displayOptions, - serverVersion + serverVersion, + getDictionaryDescription ); this.gridDisplay.addAllExpandedColumnsToSelected = true; diff --git a/packages/datalib/src/TableGridDisplay.ts b/packages/datalib/src/TableGridDisplay.ts index 044f5aa65..cd8227a2b 100644 --- a/packages/datalib/src/TableGridDisplay.ts +++ b/packages/datalib/src/TableGridDisplay.ts @@ -1,9 +1,25 @@ import _ from 'lodash'; import { filterName } from 'dbgate-tools'; import { GridDisplay, ChangeCacheFunc, DisplayColumn, DisplayedColumnInfo, ChangeConfigFunc } from './GridDisplay'; -import { TableInfo, EngineDriver, ViewInfo, ColumnInfo, NamedObjectInfo, DatabaseInfo } from 'dbgate-types'; +import { + TableInfo, + EngineDriver, + ViewInfo, + ColumnInfo, + NamedObjectInfo, + DatabaseInfo, + ForeignKeyInfo, +} from 'dbgate-types'; import { GridConfig, GridCache, createGridCache } from './GridConfig'; -import { Expression, Select, treeToSql, dumpSqlSelect } from 'dbgate-sqltree'; +import { Expression, Select, treeToSql, dumpSqlSelect, ColumnRefExpression } from 'dbgate-sqltree'; + +export interface DictionaryDescription { + expression: string; + columns: string[]; + delimiter: string; +} + +export type DictionaryDescriptionFunc = (table: TableInfo) => DictionaryDescription; export class TableGridDisplay extends GridDisplay { public table: TableInfo; @@ -19,7 +35,8 @@ export class TableGridDisplay extends GridDisplay { setCache: ChangeCacheFunc, dbinfo: DatabaseInfo, public displayOptions: any, - serverVersion + serverVersion, + public getDictionaryDescription: DictionaryDescriptionFunc = null ) { super(config, setConfig, cache, setCache, driver, dbinfo, serverVersion); @@ -61,7 +78,11 @@ export class TableGridDisplay extends GridDisplay { ?.map(col => ({ ...col, isChecked: this.isColumnChecked(col), - hintColumnName: col.foreignKey ? `hint_${col.uniqueName}` : null, + hintColumnNames: + this.getFkDictionaryDescription(col.foreignKey)?.columns?.map( + columnName => `hint_${col.uniqueName}_${columnName}` + ) || null, + hintColumnDelimiter: this.getFkDictionaryDescription(col.foreignKey)?.delimiter, isExpandable: !!col.foreignKey, })) || [] ); @@ -116,6 +137,19 @@ export class TableGridDisplay extends GridDisplay { } } + getFkDictionaryDescription(foreignKey: ForeignKeyInfo) { + if (!foreignKey) return null; + const pureName = foreignKey.refTableName; + const schemaName = foreignKey.refSchemaName; + const table = this.findTable({ schemaName, pureName }); + + if (table && table.columns && table.columns.length > 0 && table.primaryKey) { + const hintDescription = this.getDictionaryDescription(table); + return hintDescription; + } + return null; + } + addHintsToSelect(select: Select): boolean { let res = false; const groupColumns = this.groupColumns; @@ -126,17 +160,23 @@ export class TableGridDisplay extends GridDisplay { } const table = this.getFkTarget(column); if (table && table.columns && table.columns.length > 0 && table.primaryKey) { - const hintColumn = table.columns.find(x => x?.dataType?.toLowerCase()?.includes('char')); - if (hintColumn) { + // const hintColumn = table.columns.find(x => x?.dataType?.toLowerCase()?.includes('char')); + const hintDescription = this.getDictionaryDescription(table); + if (hintDescription) { 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.uniqueName}`, - source: { alias: childAlias }, - }); + select.columns.push( + ...hintDescription.columns.map( + columnName => + ({ + exprType: 'column', + columnName, + alias: `hint_${column.uniqueName}_${columnName}`, + source: { alias: childAlias }, + } as ColumnRefExpression) + ) + ); res = true; } } diff --git a/packages/web/src/datagrid/DataGridCell.svelte b/packages/web/src/datagrid/DataGridCell.svelte index 75945192a..7e509608d 100644 --- a/packages/web/src/datagrid/DataGridCell.svelte +++ b/packages/web/src/datagrid/DataGridCell.svelte @@ -113,8 +113,10 @@ {value.toString()} {/if} - {#if allowHintField && rowData && rowData[col.hintColumnName]} - {rowData[col.hintColumnName]} + {#if allowHintField && rowData && _.some(col.hintColumnNames, hintColumnName => rowData[hintColumnName])} + {col.hintColumnNames.map(hintColumnName => rowData[hintColumnName]).join(col.hintColumnDelimiter || ' ')} {/if} {#if col.foreignKey && rowData && rowData[col.uniqueName]} diff --git a/packages/web/src/datagrid/DataGridRow.svelte b/packages/web/src/datagrid/DataGridRow.svelte index 1b44b8a05..72974e05b 100644 --- a/packages/web/src/datagrid/DataGridRow.svelte +++ b/packages/web/src/datagrid/DataGridRow.svelte @@ -26,7 +26,7 @@ $: hintFieldsAllowed = visibleRealColumns .filter(col => { - if (!col.hintColumnName) return false; + if (!col.hintColumnNames) return false; if (rowStatus.modifiedFields && rowStatus.modifiedFields.has(col.uniqueName)) return false; return true; }) diff --git a/packages/web/src/datagrid/TableDataGrid.svelte b/packages/web/src/datagrid/TableDataGrid.svelte index af7da844b..9f792b7a7 100644 --- a/packages/web/src/datagrid/TableDataGrid.svelte +++ b/packages/web/src/datagrid/TableDataGrid.svelte @@ -26,6 +26,7 @@ import SqlDataGridCore from './SqlDataGridCore.svelte'; import SqlFormView from '../formview/SqlFormView.svelte'; import { getBoolSettingsValue } from '../settings/settingsTools'; + import { getDictionaryDescription } from '../utility/dictionaryDescriptionTools'; export let conid; export let database; @@ -64,7 +65,8 @@ setCache, $dbinfo, { showHintColumns: getBoolSettingsValue('dataGrid.showHintColumns', true) }, - $serverVersion + $serverVersion, + table => getDictionaryDescription(table, conid, database) ) : null; @@ -79,7 +81,8 @@ setCache, $dbinfo, { showHintColumns: getBoolSettingsValue('dataGrid.showHintColumns', true) }, - $serverVersion + $serverVersion, + table => getDictionaryDescription(table, conid, database) ) : null; diff --git a/packages/web/src/modals/DefineDictionaryDescriptionModal.svelte b/packages/web/src/modals/DefineDictionaryDescriptionModal.svelte index 12e3e9efc..ce2992b95 100644 --- a/packages/web/src/modals/DefineDictionaryDescriptionModal.svelte +++ b/packages/web/src/modals/DefineDictionaryDescriptionModal.svelte @@ -12,6 +12,7 @@ import FormProviderCore from '../forms/FormProviderCore.svelte'; import { changeDelimitedColumnList, + checkDescriptionExpression, getDictionaryDescription, parseDelimitedColumnList, saveDictionaryDescription, @@ -27,7 +28,7 @@ $: tableInfo = useTableInfo({ conid, database, schemaName, pureName }); - $: descriptionInfo = getDictionaryDescription($tableInfo, conid, database); + $: descriptionInfo = getDictionaryDescription($tableInfo, conid, database, true); const values = writable({}); @@ -78,6 +79,7 @@ { closeCurrentModal(); saveDictionaryDescription( diff --git a/packages/web/src/utility/dictionaryDescriptionTools.ts b/packages/web/src/utility/dictionaryDescriptionTools.ts index 84b0e684a..4c2a271d5 100644 --- a/packages/web/src/utility/dictionaryDescriptionTools.ts +++ b/packages/web/src/utility/dictionaryDescriptionTools.ts @@ -1,26 +1,27 @@ +import { DictionaryDescription } from 'dbgate-datalib'; import { TableInfo } from 'dbgate-types'; import _ from 'lodash'; import { getLocalStorage, setLocalStorage, removeLocalStorage } from './storageCache'; -interface DictionaryDescription { - expression: string; - columns: string[]; - delimiter: string; +function checkDescriptionColumns(columns: string[], table: TableInfo) { + return columns.length > 0 && columns.every(x => table.columns.find(y => y.columnName == x)); } -function checkDescription(desc: DictionaryDescription, table: TableInfo) { - return desc.columns.length > 0 && desc.columns.every(x => table.columns.find(y => y.columnName == x)); -} - -export function getDictionaryDescription(table: TableInfo, conid: string, database: string): DictionaryDescription { +export function getDictionaryDescription( + table: TableInfo, + conid: string, + database: string, + skipCheckSaved: boolean = false +): DictionaryDescription { const keySpecific = `dictionary_spec_${table.schemaName}||${table.pureName}||${conid}||${database}`; const keyCommon = `dictionary_spec_${table.schemaName}||${table.pureName}`; const cachedSpecific = getLocalStorage(keySpecific); const cachedCommon = getLocalStorage(keyCommon); - if (cachedSpecific && checkDescription(cachedSpecific, table)) return cachedSpecific; - if (cachedCommon && checkDescription(cachedCommon, table)) return cachedCommon; + if (cachedSpecific && (skipCheckSaved || checkDescriptionColumns(cachedSpecific.columns, table))) + return cachedSpecific; + if (cachedCommon && (skipCheckSaved || checkDescriptionColumns(cachedCommon.columns, table))) return cachedCommon; const descColumn = table.columns.find(x => x?.dataType?.toLowerCase()?.includes('char')); if (descColumn) { @@ -34,10 +35,16 @@ export function getDictionaryDescription(table: TableInfo, conid: string, databa return null; } -export function parseDelimitedColumnList(columns) { +export function parseDelimitedColumnList(columns): string[] { return _.compact((columns || '').split(',').map(x => x.trim())); } +export function checkDescriptionExpression(expression: string, table: TableInfo) { + if (!expression) return false; + if (!table) return false; + return checkDescriptionColumns(parseDelimitedColumnList(expression), table); +} + export function changeDelimitedColumnList(columns, columnName, isChecked) { const parsed = parseDelimitedColumnList(columns); const includes = parsed.includes(columnName);