query designer SQL generator

This commit is contained in:
Jan Prochazka
2020-12-28 16:31:21 +01:00
parent c10c6afbc5
commit 60fd1c38ae
3 changed files with 121 additions and 19 deletions

View File

@@ -2,6 +2,7 @@ import { SqlDumper } from 'dbgate-types';
import { Condition, BinaryCondition } from './types'; import { Condition, BinaryCondition } from './types';
import { dumpSqlExpression } from './dumpSqlExpression'; import { dumpSqlExpression } from './dumpSqlExpression';
import { link } from 'fs'; import { link } from 'fs';
import { dumpSqlSelect } from './dumpSqlCommand';
export function dumpSqlCondition(dmp: SqlDumper, condition: Condition) { export function dumpSqlCondition(dmp: SqlDumper, condition: Condition) {
switch (condition.conditionType) { switch (condition.conditionType) {
@@ -30,7 +31,7 @@ export function dumpSqlCondition(dmp: SqlDumper, condition: Condition) {
break; break;
case 'and': case 'and':
case 'or': case 'or':
dmp.putCollection(` ^${condition.conditionType} `, condition.conditions, cond => { dmp.putCollection(` ^${condition.conditionType} `, condition.conditions, (cond) => {
dmp.putRaw('('); dmp.putRaw('(');
dumpSqlCondition(dmp, cond); dumpSqlCondition(dmp, cond);
dmp.putRaw(')'); dmp.putRaw(')');
@@ -51,5 +52,15 @@ export function dumpSqlCondition(dmp: SqlDumper, condition: Condition) {
dumpSqlCondition(dmp, condition.condition); dumpSqlCondition(dmp, condition.condition);
dmp.put(')'); dmp.put(')');
break; break;
case 'exists':
dmp.put('^exists (');
dumpSqlSelect(dmp, condition.subQuery);
dmp.put(')');
break;
case 'notExists':
dmp.put('^not ^exists (');
dumpSqlSelect(dmp, condition.subQuery);
dmp.put(')');
break;
} }
} }

View File

@@ -82,7 +82,23 @@ export interface CompoudCondition {
conditions: Condition[]; conditions: Condition[];
} }
export type Condition = BinaryCondition | NotCondition | TestCondition | CompoudCondition | LikeCondition; export interface ExistsCondition {
conditionType: 'exists';
subQuery: Select;
}
export interface NotExistsCondition {
conditionType: 'notExists';
subQuery: Select;
}
export type Condition =
| BinaryCondition
| NotCondition
| TestCondition
| CompoudCondition
| LikeCondition
| ExistsCondition
| NotExistsCondition;
export interface Source { export interface Source {
name?: NamedObjectInfo; name?: NamedObjectInfo;

View File

@@ -1,5 +1,5 @@
import _ from 'lodash'; import _ from 'lodash';
import { dumpSqlSelect, Select, JoinType, Condition } from 'dbgate-sqltree'; import { dumpSqlSelect, Select, JoinType, Condition, Relation } from 'dbgate-sqltree';
import { EngineDriver } from 'dbgate-types'; import { EngineDriver } from 'dbgate-types';
import { DesignerInfo, DesignerTableInfo, DesignerReferenceInfo, DesignerJoinType } from './types'; import { DesignerInfo, DesignerTableInfo, DesignerReferenceInfo, DesignerJoinType } from './types';
@@ -37,18 +37,51 @@ function findJoinType(
table: DesignerTableInfo, table: DesignerTableInfo,
dumpedTables: DesignerTableInfo[], dumpedTables: DesignerTableInfo[],
references: DesignerReferenceInfo[], references: DesignerReferenceInfo[],
joinTypes: DesignerJoinType[] joinTypes = ['INNER JOIN', 'LEFT JOIN', 'RIGHT JOIN', 'FULL OUTER JOIN', 'WHERE EXISTS', 'WHERE NOT EXISTS']
): DesignerJoinType { ): DesignerJoinType {
const dumpedTableIds = dumpedTables.map((x) => x.designerId); const dumpedTableIds = dumpedTables.map((x) => x.designerId);
const reference = references.find( const reference = references.find(
(x) => (x) =>
(x.sourceId == table.designerId && dumpedTableIds.includes(x.targetId)) || joinTypes.includes(x.joinType) &&
(x.targetId == table.designerId && dumpedTableIds.includes(x.sourceId)) ((x.sourceId == table.designerId && dumpedTableIds.includes(x.targetId)) ||
(x.targetId == table.designerId && dumpedTableIds.includes(x.sourceId)))
); );
if (reference) return reference.joinType || 'CROSS JOIN'; if (reference) return reference.joinType || 'CROSS JOIN';
return 'CROSS JOIN'; return 'CROSS JOIN';
} }
function sortTablesByReferences(
dumpedTables: DesignerTableInfo[],
tables: DesignerTableInfo[],
references: DesignerReferenceInfo[],
joinTypes = ['INNER JOIN', 'LEFT JOIN', 'RIGHT JOIN', 'FULL OUTER JOIN', 'WHERE EXISTS', 'WHERE NOT EXISTS']
) {
const res = [];
const dumpedTableIds = dumpedTables.map((x) => x.designerId);
const toAdd = [...tables];
while (toAdd.length > 0) {
let found = false;
for (const test of toAdd) {
const reference = references.find(
(x) =>
joinTypes.includes(x.joinType) &&
((x.sourceId == test.designerId && dumpedTableIds.includes(x.targetId)) ||
(x.targetId == test.designerId && dumpedTableIds.includes(x.sourceId)))
);
if (reference) {
res.push(test);
_.remove(toAdd, (x) => x == test);
dumpedTableIds.push(test.designerId);
found = true;
break;
}
}
if (!found) break;
}
res.push(...toAdd);
return res;
}
function findConditions( function findConditions(
table: DesignerTableInfo, table: DesignerTableInfo,
dumpedTables: DesignerTableInfo[], dumpedTables: DesignerTableInfo[],
@@ -90,6 +123,24 @@ function findConditions(
return res; return res;
} }
function addRelations(
relations: Relation[],
tables: DesignerTableInfo[],
dumpedTables: DesignerTableInfo[],
designer: DesignerInfo
) {
for (const table of tables) {
if (dumpedTables.includes(table)) continue;
relations.push({
name: table,
alias: table.alias,
joinType: findJoinType(table, dumpedTables, designer.references) as JoinType,
conditions: findConditions(table, dumpedTables, designer.references, designer.tables),
});
dumpedTables.push(table);
}
}
export default function generateDesignedQuery(designer: DesignerInfo, engine: EngineDriver) { export default function generateDesignedQuery(designer: DesignerInfo, engine: EngineDriver) {
const { tables, columns, references } = designer; const { tables, columns, references } = designer;
const primaryTable = findPrimaryTable(designer.tables); const primaryTable = findPrimaryTable(designer.tables);
@@ -111,6 +162,7 @@ export default function generateDesignedQuery(designer: DesignerInfo, engine: En
}; };
const dumpedTables = [primaryTable]; const dumpedTables = [primaryTable];
const conditions: Condition[] = [];
for (const component of components) { for (const component of components) {
const subComponents = groupByComponents( const subComponents = groupByComponents(
component, component,
@@ -119,23 +171,46 @@ export default function generateDesignedQuery(designer: DesignerInfo, engine: En
primaryTable primaryTable
); );
for (const subComponent of subComponents) { for (const subComponent of subComponents) {
for (const table of subComponent) { const sortedSubComponent = sortTablesByReferences(dumpedTables, subComponent, designer.references);
if (dumpedTables.includes(table)) continue; const table0 = sortedSubComponent[0];
select.from.relations.push({ const joinType0 = findJoinType(table0, dumpedTables, designer.references);
name: table, if (joinType0 == 'WHERE EXISTS' || joinType0 == 'WHERE NOT EXISTS') {
alias: table.alias, const subselect: Select = {
joinType: findJoinType(table, dumpedTables, designer.references, [ commandType: 'select',
'INNER JOIN', from: {
'LEFT JOIN', name: table0,
'RIGHT JOIN', alias: table0.alias,
'FULL OUTER JOIN', relations: [],
]) as JoinType, },
conditions: findConditions(table, dumpedTables, designer.references, designer.tables), };
dumpedTables.push(table0);
addRelations(subselect.from.relations, sortedSubComponent, dumpedTables, designer);
conditions.push({
conditionType: joinType0 == 'WHERE EXISTS' ? 'exists' : 'notExists',
subQuery: subselect,
}); });
dumpedTables.push(table); } else {
addRelations(select.from.relations, sortedSubComponent, dumpedTables, designer);
// for (const table of sortedSubComponent) {
// if (dumpedTables.includes(table)) continue;
// select.from.relations.push({
// name: table,
// alias: table.alias,
// joinType: findJoinType(table, dumpedTables, designer.references) as JoinType,
// conditions: findConditions(table, dumpedTables, designer.references, designer.tables),
// });
// dumpedTables.push(table);
// }
} }
} }
} }
if (conditions.length > 0) {
select.where = {
conditionType: 'and',
conditions,
};
}
const dmp = engine.createDumper(); const dmp = engine.createDumper();
dumpSqlSelect(dmp, select); dumpSqlSelect(dmp, select);