mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-28 19:56:00 +00:00
generated designer query SQL
This commit is contained in:
@@ -130,6 +130,7 @@ export default function Designer({ value, onChange }) {
|
||||
designerId: uuidv1(),
|
||||
sourceId: source.designerId,
|
||||
targetId: target.designerId,
|
||||
joinType: 'INNER JOIN',
|
||||
columns: [
|
||||
{
|
||||
source: source.columnName,
|
||||
|
||||
@@ -46,6 +46,7 @@ function ReferenceContextMenu({ remove, setJoinType }) {
|
||||
<DropDownMenuItem onClick={() => setJoinType('LEFT JOIN')}>Set LEFT JOIN</DropDownMenuItem>
|
||||
<DropDownMenuItem onClick={() => setJoinType('RIGHT JOIN')}>Set RIGHT JOIN</DropDownMenuItem>
|
||||
<DropDownMenuItem onClick={() => setJoinType('FULL OUTER JOIN')}>Set FULL OUTER JOIN</DropDownMenuItem>
|
||||
<DropDownMenuItem onClick={() => setJoinType('CROSS JOIN')}>Set CROSS JOIN</DropDownMenuItem>
|
||||
<DropDownMenuItem onClick={() => setJoinType('WHERE EXISTS')}>Set WHERE EXISTS</DropDownMenuItem>
|
||||
<DropDownMenuItem onClick={() => setJoinType('WHERE NOT EXISTS')}>Set WHERE NOT EXISTS</DropDownMenuItem>
|
||||
</>
|
||||
@@ -159,7 +160,7 @@ export default function DesignerReference({
|
||||
onContextMenu={handleContextMenu}
|
||||
>
|
||||
<ReferenceText theme={theme}>
|
||||
{_.snakeCase(joinType || 'INNER JOIN')
|
||||
{_.snakeCase(joinType || 'CROSS JOIN')
|
||||
.replace('_', '\xa0')
|
||||
.replace('_', '\xa0')}
|
||||
</ReferenceText>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { TableInfo } from 'dbgate-types';
|
||||
|
||||
type DesignerTableInfo = TableInfo & { designerId: string };
|
||||
import { DesignerTableInfo } from "./types";
|
||||
|
||||
export default class DomTableRef {
|
||||
domTable: Element;
|
||||
|
||||
143
packages/web/src/designer/generateDesignedQuery.ts
Normal file
143
packages/web/src/designer/generateDesignedQuery.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
import _ from 'lodash';
|
||||
import { dumpSqlSelect, Select, JoinType, Condition } from 'dbgate-sqltree';
|
||||
import { EngineDriver } from 'dbgate-types';
|
||||
import { DesignerInfo, DesignerTableInfo, DesignerReferenceInfo, DesignerJoinType } from './types';
|
||||
|
||||
function groupByComponents(
|
||||
tables: DesignerTableInfo[],
|
||||
references: DesignerReferenceInfo[],
|
||||
joinTypes: string[],
|
||||
primaryTable: DesignerTableInfo
|
||||
) {
|
||||
let components = tables.map((table) => [table]);
|
||||
for (const ref of references) {
|
||||
if (joinTypes.includes(ref.joinType)) {
|
||||
const comp1 = components.find((comp) => comp.find((t) => t.designerId == ref.sourceId));
|
||||
const comp2 = components.find((comp) => comp.find((t) => t.designerId == ref.targetId));
|
||||
if (comp1 && comp2 && comp1 != comp2) {
|
||||
// join components
|
||||
components = [...components.filter((x) => x != comp1 && x != comp2), [...comp1, ...comp2]];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (primaryTable) {
|
||||
const primaryComponent = components.find((comp) => comp.find((t) => t == primaryTable));
|
||||
if (primaryComponent) {
|
||||
components = [primaryComponent, ...components.filter((x) => x != primaryComponent)];
|
||||
}
|
||||
}
|
||||
return components;
|
||||
}
|
||||
|
||||
function findPrimaryTable(tables: DesignerTableInfo[]) {
|
||||
return _.minBy(tables, (x) => x.left + x.top);
|
||||
}
|
||||
|
||||
function findJoinType(
|
||||
table: DesignerTableInfo,
|
||||
dumpedTables: DesignerTableInfo[],
|
||||
references: DesignerReferenceInfo[],
|
||||
joinTypes: DesignerJoinType[]
|
||||
): DesignerJoinType {
|
||||
const dumpedTableIds = dumpedTables.map((x) => x.designerId);
|
||||
const reference = references.find(
|
||||
(x) =>
|
||||
(x.sourceId == table.designerId && dumpedTableIds.includes(x.targetId)) ||
|
||||
(x.targetId == table.designerId && dumpedTableIds.includes(x.sourceId))
|
||||
);
|
||||
if (reference) return reference.joinType || 'CROSS JOIN';
|
||||
return 'CROSS JOIN';
|
||||
}
|
||||
|
||||
function findConditions(
|
||||
table: DesignerTableInfo,
|
||||
dumpedTables: DesignerTableInfo[],
|
||||
references: DesignerReferenceInfo[],
|
||||
tables: DesignerTableInfo[]
|
||||
): Condition[] {
|
||||
const dumpedTableIds = dumpedTables.map((x) => x.designerId);
|
||||
const res = [];
|
||||
for (const reference of references.filter(
|
||||
(x) =>
|
||||
(x.sourceId == table.designerId && dumpedTableIds.includes(x.targetId)) ||
|
||||
(x.targetId == table.designerId && dumpedTableIds.includes(x.sourceId))
|
||||
)) {
|
||||
const sourceTable = tables.find((x) => x.designerId == reference.sourceId);
|
||||
const targetTable = tables.find((x) => x.designerId == reference.targetId);
|
||||
res.push(
|
||||
...reference.columns.map((col) => ({
|
||||
conditionType: 'binary',
|
||||
operator: '=',
|
||||
left: {
|
||||
exprType: 'column',
|
||||
columnName: col.source,
|
||||
source: {
|
||||
name: sourceTable,
|
||||
alias: sourceTable.alias,
|
||||
},
|
||||
},
|
||||
right: {
|
||||
exprType: 'column',
|
||||
columnName: col.target,
|
||||
source: {
|
||||
name: targetTable,
|
||||
alias: targetTable.alias,
|
||||
},
|
||||
},
|
||||
}))
|
||||
);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
export default function generateDesignedQuery(designer: DesignerInfo, engine: EngineDriver) {
|
||||
const { tables, columns, references } = designer;
|
||||
const primaryTable = findPrimaryTable(designer.tables);
|
||||
if (!primaryTable) return '';
|
||||
const components = groupByComponents(
|
||||
designer.tables,
|
||||
designer.references,
|
||||
['INNER JOIN', 'LEFT JOIN', 'RIGHT JOIN', 'FULL OUTER JOIN', 'WHERE EXISTS', 'WHERE NOT EXISTS'],
|
||||
primaryTable
|
||||
);
|
||||
|
||||
const select: Select = {
|
||||
commandType: 'select',
|
||||
from: {
|
||||
name: primaryTable,
|
||||
alias: primaryTable.alias,
|
||||
relations: [],
|
||||
},
|
||||
};
|
||||
|
||||
const dumpedTables = [primaryTable];
|
||||
for (const component of components) {
|
||||
const subComponents = groupByComponents(
|
||||
component,
|
||||
designer.references,
|
||||
['INNER JOIN', 'LEFT JOIN', 'RIGHT JOIN', 'FULL OUTER JOIN'],
|
||||
primaryTable
|
||||
);
|
||||
for (const subComponent of subComponents) {
|
||||
for (const table of subComponent) {
|
||||
if (dumpedTables.includes(table)) continue;
|
||||
select.from.relations.push({
|
||||
name: table,
|
||||
alias: table.alias,
|
||||
joinType: findJoinType(table, dumpedTables, designer.references, [
|
||||
'INNER JOIN',
|
||||
'LEFT JOIN',
|
||||
'RIGHT JOIN',
|
||||
'FULL OUTER JOIN',
|
||||
]) as JoinType,
|
||||
conditions: findConditions(table, dumpedTables, designer.references, designer.tables),
|
||||
});
|
||||
dumpedTables.push(table);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const dmp = engine.createDumper();
|
||||
dumpSqlSelect(dmp, select);
|
||||
return dmp.s;
|
||||
}
|
||||
41
packages/web/src/designer/types.ts
Normal file
41
packages/web/src/designer/types.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { JoinType } from 'dbgate-sqltree';
|
||||
import { TableInfo } from 'dbgate-types';
|
||||
|
||||
export type DesignerTableInfo = TableInfo & {
|
||||
designerId: string;
|
||||
alias?: string;
|
||||
left: number;
|
||||
top: number;
|
||||
};
|
||||
|
||||
export type DesignerJoinType = JoinType | 'WHERE EXISTS' | 'WHERE NOT EXISTS';
|
||||
|
||||
export type DesignerReferenceInfo = {
|
||||
designerId: string;
|
||||
joinType: DesignerJoinType;
|
||||
sourceId: string;
|
||||
targetId: string;
|
||||
columns: {
|
||||
source: string;
|
||||
target: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
export type DesignerColumnInfo = {
|
||||
designerId: string;
|
||||
columnName: string;
|
||||
alias?: string;
|
||||
isGrouped?: boolean;
|
||||
isOutput?: boolean;
|
||||
filter: string;
|
||||
};
|
||||
|
||||
export type DesignerInfo = {
|
||||
tables: DesignerTableInfo[];
|
||||
columns: DesignerColumnInfo[];
|
||||
references: DesignerReferenceInfo[];
|
||||
};
|
||||
|
||||
// export type DesignerComponent = {
|
||||
// tables: DesignerTableInfo[];
|
||||
// };
|
||||
Reference in New Issue
Block a user