mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-30 01:03:58 +00:00
query design ops, add reference
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { DatabaseInfo, DatabaseInfoObjects } from 'dbgate-types';
|
import { ColumnInfo, DatabaseInfo, DatabaseInfoObjects, TableInfo } from 'dbgate-types';
|
||||||
|
|
||||||
export function fullNameFromString(name) {
|
export function fullNameFromString(name) {
|
||||||
const m = name.match(/\[([^\]]+)\]\.\[([^\]]+)\]/);
|
const m = name.match(/\[([^\]]+)\]\.\[([^\]]+)\]/);
|
||||||
@@ -45,3 +45,7 @@ export function findObjectLike(
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return dbinfo[objectTypeField].find((x) => equalStringLike(x.pureName, pureName));
|
return dbinfo[objectTypeField].find((x) => equalStringLike(x.pureName, pureName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function findForeignKeyForColumn(table: TableInfo, column: ColumnInfo) {
|
||||||
|
return (table.foreignKeys || []).find((fk) => fk.columns.find((col) => col.columnName == column.columnName));
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import useTheme from '../theme/useTheme';
|
|||||||
import DesignerReference from './DesignerReference';
|
import DesignerReference from './DesignerReference';
|
||||||
import cleanupDesignColumns from './cleanupDesignColumns';
|
import cleanupDesignColumns from './cleanupDesignColumns';
|
||||||
import { isConnectedByReference } from './designerTools';
|
import { isConnectedByReference } from './designerTools';
|
||||||
|
import { getTableInfo } from '../utility/metadataLoaders';
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@@ -20,7 +21,7 @@ const Canvas = styled.div`
|
|||||||
position: relative;
|
position: relative;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default function Designer({ value, onChange }) {
|
export default function Designer({ value, onChange, conid, database }) {
|
||||||
const { tables, references } = value || {};
|
const { tables, references } = value || {};
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
@@ -39,10 +40,11 @@ export default function Designer({ value, onChange }) {
|
|||||||
json.designerId = uuidv1();
|
json.designerId = uuidv1();
|
||||||
json.left = e.clientX - rect.left;
|
json.left = e.clientX - rect.left;
|
||||||
json.top = e.clientY - rect.top;
|
json.top = e.clientY - rect.top;
|
||||||
onChange({
|
|
||||||
...value,
|
onChange((current) => ({
|
||||||
tables: [...(tables || []), json],
|
...current,
|
||||||
});
|
tables: [...(current.tables || []), json],
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const changeTable = React.useCallback(
|
const changeTable = React.useCallback(
|
||||||
@@ -70,6 +72,9 @@ export default function Designer({ value, onChange }) {
|
|||||||
onChange((current) => ({
|
onChange((current) => ({
|
||||||
...current,
|
...current,
|
||||||
tables: (current.tables || []).filter((x) => x.designerId != table.designerId),
|
tables: (current.tables || []).filter((x) => x.designerId != table.designerId),
|
||||||
|
references: (current.references || []).filter(
|
||||||
|
(x) => x.sourceId != table.designerId && x.targetId != table.designerId
|
||||||
|
),
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
[onChange]
|
[onChange]
|
||||||
@@ -144,6 +149,45 @@ export default function Designer({ value, onChange }) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleAddReferenceByColumn = async (designerId, foreignKey) => {
|
||||||
|
const toTable = await getTableInfo({
|
||||||
|
conid,
|
||||||
|
database,
|
||||||
|
pureName: foreignKey.refTableName,
|
||||||
|
schemaName: foreignKey.refSchemaName,
|
||||||
|
});
|
||||||
|
const newTableDesignerId = uuidv1();
|
||||||
|
onChange((current) => {
|
||||||
|
const fromTable = (current.tables || []).find((x) => x.designerId == designerId);
|
||||||
|
if (!fromTable) return;
|
||||||
|
return {
|
||||||
|
...current,
|
||||||
|
tables: [
|
||||||
|
...(current.tables || []),
|
||||||
|
{
|
||||||
|
...toTable,
|
||||||
|
left: fromTable.left + 300,
|
||||||
|
top: fromTable.top + 50,
|
||||||
|
designerId: newTableDesignerId,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
references: [
|
||||||
|
...(current.references || []),
|
||||||
|
{
|
||||||
|
designerId: uuidv1(),
|
||||||
|
sourceId: fromTable.designerId,
|
||||||
|
targetId: newTableDesignerId,
|
||||||
|
joinType: 'INNER JOIN',
|
||||||
|
columns: foreignKey.columns.map((col) => ({
|
||||||
|
source: col.columnName,
|
||||||
|
target: col.refColumnName,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const handleSelectColumn = React.useCallback(
|
const handleSelectColumn = React.useCallback(
|
||||||
(column) => {
|
(column) => {
|
||||||
onChange((current) => ({
|
onChange((current) => ({
|
||||||
@@ -211,6 +255,7 @@ export default function Designer({ value, onChange }) {
|
|||||||
onCreateReference={handleCreateReference}
|
onCreateReference={handleCreateReference}
|
||||||
onSelectColumn={handleSelectColumn}
|
onSelectColumn={handleSelectColumn}
|
||||||
onChangeColumn={handleChangeColumn}
|
onChangeColumn={handleChangeColumn}
|
||||||
|
onAddReferenceByColumn={handleAddReferenceByColumn}
|
||||||
table={table}
|
table={table}
|
||||||
onChangeTable={changeTable}
|
onChangeTable={changeTable}
|
||||||
onBringToFront={bringToFront}
|
onBringToFront={bringToFront}
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import { findForeignKeyForColumn } from 'dbgate-tools';
|
||||||
import ColumnLabel from '../datagrid/ColumnLabel';
|
import ColumnLabel from '../datagrid/ColumnLabel';
|
||||||
import { FontIcon } from '../icons';
|
import { FontIcon } from '../icons';
|
||||||
import useTheme from '../theme/useTheme';
|
import useTheme from '../theme/useTheme';
|
||||||
import DomTableRef from './DomTableRef';
|
import DomTableRef from './DomTableRef';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { CheckboxField } from '../utility/inputs';
|
import { CheckboxField } from '../utility/inputs';
|
||||||
|
import { useShowMenu } from '../modals/showMenu';
|
||||||
|
import { DropDownMenuDivider, DropDownMenuItem } from '../modals/DropDownMenu';
|
||||||
|
import useShowModal from '../modals/showModal';
|
||||||
|
import InputTextModal from '../modals/InputTextModal';
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -80,12 +85,50 @@ const ColumnLine = styled.div`
|
|||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
function TableContextMenu({ remove, setTableAlias, removeTableAlias }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DropDownMenuItem onClick={remove}>Remove</DropDownMenuItem>
|
||||||
|
<DropDownMenuDivider />
|
||||||
|
<DropDownMenuItem onClick={setTableAlias}>Set table alias</DropDownMenuItem>
|
||||||
|
{!!removeTableAlias && <DropDownMenuItem onClick={removeTableAlias}>Remove table alias</DropDownMenuItem>}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ColumnContextMenu({ setSortOrder, addReference }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DropDownMenuItem onClick={() => setSortOrder(1)}>Sort ascending</DropDownMenuItem>
|
||||||
|
<DropDownMenuItem onClick={() => setSortOrder(-1)}>Sort descending</DropDownMenuItem>
|
||||||
|
<DropDownMenuItem onClick={() => setSortOrder(0)}>Unsort</DropDownMenuItem>
|
||||||
|
{!!addReference && <DropDownMenuItem onClick={addReference}>Add reference</DropDownMenuItem>}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ColumnDesignerIcons({ column, designerId, designer }) {
|
||||||
|
const designerColumn = (designer.columns || []).find(
|
||||||
|
(x) => x.designerId == designerId && x.columnName == column.columnName
|
||||||
|
);
|
||||||
|
if (!designerColumn) return null;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{!!designerColumn.filter && <FontIcon icon="img filter" />}
|
||||||
|
{designerColumn.sortOrder > 0 && <FontIcon icon="img sort-asc" />}
|
||||||
|
{designerColumn.sortOrder < 0 && <FontIcon icon="img sort-desc" />}
|
||||||
|
{!!designerColumn.isGrouped && <FontIcon icon="img group" />}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function DesignerTable({
|
export default function DesignerTable({
|
||||||
table,
|
table,
|
||||||
onChangeTable,
|
onChangeTable,
|
||||||
onBringToFront,
|
onBringToFront,
|
||||||
onRemoveTable,
|
onRemoveTable,
|
||||||
onCreateReference,
|
onCreateReference,
|
||||||
|
onAddReferenceByColumn,
|
||||||
onSelectColumn,
|
onSelectColumn,
|
||||||
onChangeColumn,
|
onChangeColumn,
|
||||||
sourceDragColumn,
|
sourceDragColumn,
|
||||||
@@ -97,11 +140,13 @@ export default function DesignerTable({
|
|||||||
setChangeToken,
|
setChangeToken,
|
||||||
designer,
|
designer,
|
||||||
}) {
|
}) {
|
||||||
const { pureName, columns, left, top, designerId } = table;
|
const { pureName, columns, left, top, designerId, alias } = table;
|
||||||
const [movingPosition, setMovingPosition] = React.useState(null);
|
const [movingPosition, setMovingPosition] = React.useState(null);
|
||||||
const movingPositionRef = React.useRef(null);
|
const movingPositionRef = React.useRef(null);
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const domObjectsRef = React.useRef({});
|
const domObjectsRef = React.useRef({});
|
||||||
|
const showMenu = useShowMenu();
|
||||||
|
const showModal = useShowModal();
|
||||||
|
|
||||||
const moveStartXRef = React.useRef(null);
|
const moveStartXRef = React.useRef(null);
|
||||||
const moveStartYRef = React.useRef(null);
|
const moveStartYRef = React.useRef(null);
|
||||||
@@ -185,6 +230,71 @@ export default function DesignerTable({
|
|||||||
changeTokenDebounced.current();
|
changeTokenDebounced.current();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSetTableAlias = () => {
|
||||||
|
showModal((modalState) => (
|
||||||
|
<InputTextModal
|
||||||
|
modalState={modalState}
|
||||||
|
value={alias || ''}
|
||||||
|
label="New alias"
|
||||||
|
header="Set table alias"
|
||||||
|
onConfirm={(newAlias) => {
|
||||||
|
onChangeTable({
|
||||||
|
...table,
|
||||||
|
alias: newAlias,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleHeaderContextMenu = (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
showMenu(
|
||||||
|
event.pageX,
|
||||||
|
event.pageY,
|
||||||
|
<TableContextMenu
|
||||||
|
remove={() => onRemoveTable({ designerId })}
|
||||||
|
setTableAlias={handleSetTableAlias}
|
||||||
|
removeTableAlias={
|
||||||
|
alias
|
||||||
|
? () =>
|
||||||
|
onChangeTable({
|
||||||
|
...table,
|
||||||
|
alias: null,
|
||||||
|
})
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleColumnContextMenu = (column) => (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const foreignKey = findForeignKeyForColumn(table, column);
|
||||||
|
showMenu(
|
||||||
|
event.pageX,
|
||||||
|
event.pageY,
|
||||||
|
<ColumnContextMenu
|
||||||
|
setSortOrder={(sortOrder) => {
|
||||||
|
onChangeColumn(
|
||||||
|
{
|
||||||
|
...column,
|
||||||
|
designerId,
|
||||||
|
},
|
||||||
|
(col) => ({ ...col, sortOrder })
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
addReference={
|
||||||
|
foreignKey
|
||||||
|
? () => {
|
||||||
|
onAddReferenceByColumn(designerId, foreignKey);
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper
|
<Wrapper
|
||||||
theme={theme}
|
theme={theme}
|
||||||
@@ -195,8 +305,8 @@ export default function DesignerTable({
|
|||||||
onMouseDown={() => onBringToFront(table)}
|
onMouseDown={() => onBringToFront(table)}
|
||||||
ref={(dom) => dispatchDomColumn('', dom)}
|
ref={(dom) => dispatchDomColumn('', dom)}
|
||||||
>
|
>
|
||||||
<Header onMouseDown={headerMouseDown} theme={theme}>
|
<Header onMouseDown={headerMouseDown} theme={theme} onContextMenu={handleHeaderContextMenu}>
|
||||||
<HeaderLabel>{pureName}</HeaderLabel>
|
<HeaderLabel>{alias || pureName}</HeaderLabel>
|
||||||
<CloseWrapper onClick={() => onRemoveTable(table)} theme={theme}>
|
<CloseWrapper onClick={() => onRemoveTable(table)} theme={theme}>
|
||||||
<FontIcon icon="icon close" />
|
<FontIcon icon="icon close" />
|
||||||
</CloseWrapper>
|
</CloseWrapper>
|
||||||
@@ -204,6 +314,7 @@ export default function DesignerTable({
|
|||||||
<ColumnsWrapper>
|
<ColumnsWrapper>
|
||||||
{(columns || []).map((column) => (
|
{(columns || []).map((column) => (
|
||||||
<ColumnLine
|
<ColumnLine
|
||||||
|
onContextMenu={handleColumnContextMenu(column)}
|
||||||
key={column.columnName}
|
key={column.columnName}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
draggable
|
draggable
|
||||||
@@ -278,7 +389,8 @@ export default function DesignerTable({
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ColumnLabel {...column} forceIcon />
|
<ColumnLabel {...column} foreignKey={findForeignKeyForColumn(table, column)} forceIcon />
|
||||||
|
<ColumnDesignerIcons column={column} designerId={designerId} designer={designer} />
|
||||||
</ColumnLine>
|
</ColumnLine>
|
||||||
))}
|
))}
|
||||||
</ColumnsWrapper>
|
</ColumnsWrapper>
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ import styled from 'styled-components';
|
|||||||
import Designer from './Designer';
|
import Designer from './Designer';
|
||||||
|
|
||||||
export default function QueryDesigner({ value, conid, database, engine, onChange }) {
|
export default function QueryDesigner({ value, conid, database, engine, onChange }) {
|
||||||
return <Designer value={value} onChange={onChange}></Designer>;
|
return <Designer value={value} onChange={onChange} conid={conid} database={database}></Designer>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export function findConnectingReference(
|
|||||||
tables2: DesignerTableInfo[],
|
tables2: DesignerTableInfo[],
|
||||||
additionalCondition: (ref: DesignerReferenceInfo) => boolean
|
additionalCondition: (ref: DesignerReferenceInfo) => boolean
|
||||||
) {
|
) {
|
||||||
for (const ref of designer.references) {
|
for (const ref of designer.references || []) {
|
||||||
if (additionalCondition(ref) && referenceIsConnecting(ref, tables1, tables2)) {
|
if (additionalCondition(ref) && referenceIsConnecting(ref, tables1, tables2)) {
|
||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
@@ -119,6 +119,7 @@ export function isConnectedByReference(
|
|||||||
table2: { designerId: string },
|
table2: { designerId: string },
|
||||||
withoutRef: { designerId: string }
|
withoutRef: { designerId: string }
|
||||||
) {
|
) {
|
||||||
|
if (!designer.references) return false;
|
||||||
const creator = new DesignerComponentCreator({
|
const creator = new DesignerComponentCreator({
|
||||||
...designer,
|
...designer,
|
||||||
references: withoutRef
|
references: withoutRef
|
||||||
|
|||||||
@@ -85,6 +85,8 @@ const iconNames = {
|
|||||||
|
|
||||||
'img reference': 'mdi mdi-link-box',
|
'img reference': 'mdi mdi-link-box',
|
||||||
'img link': 'mdi mdi-link',
|
'img link': 'mdi mdi-link',
|
||||||
|
'img filter': 'mdi mdi-filter',
|
||||||
|
'img group': 'mdi mdi-group',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function FontIcon({ icon, className = '', ...other }) {
|
export function FontIcon({ icon, className = '', ...other }) {
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export default function QueryDesignTab({
|
|||||||
sesid,
|
sesid,
|
||||||
sql: sqlPreview,
|
sql: sqlPreview,
|
||||||
});
|
});
|
||||||
}, [busy]);
|
}, [busy, conid, sessionId, database, sqlPreview]);
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
axios.post('sessions/cancel', {
|
axios.post('sessions/cancel', {
|
||||||
@@ -118,12 +118,15 @@ export default function QueryDesignTab({
|
|||||||
setBusy(false);
|
setBusy(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleKeyDown = React.useCallback((e) => {
|
const handleKeyDown = React.useCallback(
|
||||||
if (e.keyCode == keycodes.f5) {
|
(e) => {
|
||||||
e.preventDefault();
|
if (e.keyCode == keycodes.f5) {
|
||||||
handleExecute();
|
e.preventDefault();
|
||||||
}
|
handleExecute();
|
||||||
}, []);
|
}
|
||||||
|
},
|
||||||
|
[handleExecute]
|
||||||
|
);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (tabVisible) {
|
if (tabVisible) {
|
||||||
|
|||||||
Reference in New Issue
Block a user