diff --git a/packages/web/src/designer/DesignerQueryDumper.ts b/packages/web/src/designer/DesignerQueryDumper.ts index d25bb2f22..6d367024b 100644 --- a/packages/web/src/designer/DesignerQueryDumper.ts +++ b/packages/web/src/designer/DesignerQueryDumper.ts @@ -8,6 +8,7 @@ import { mergeConditions, Source, ResultField, + Expression, } from 'dbgate-sqltree'; import { EngineDriver } from 'dbgate-types'; import { DesignerInfo, DesignerTableInfo, DesignerReferenceInfo, DesignerJoinType } from './types'; @@ -120,7 +121,7 @@ export class DesignerQueryDumper { select.having, _.cloneDeepWith(condition, expr => { if (expr.exprType == 'placeholder') { - return this.getColumnOutputExpression(column, selectIsGrouped); + return this.getColumnResultField(column, selectIsGrouped); } }) ); @@ -128,10 +129,29 @@ export class DesignerQueryDumper { } } - getColumnOutputExpression(col, selectIsGrouped): ResultField { + getColumnExpression(col): Expression { const source = findQuerySource(this.designer, col.designerId); + const { columnName, isCustomExpression, customExpression } = col; + + const res: Expression = isCustomExpression + ? { + exprType: 'raw', + sql: customExpression, + } + : { + exprType: 'column', + columnName, + source, + }; + return res; + } + + getColumnResultField(col, selectIsGrouped): ResultField { const { columnName } = col; let { alias } = col; + + const exprCore = this.getColumnExpression(col); + if (selectIsGrouped && !col.isGrouped) { // use aggregate const aggregate = col.aggregate == null || col.aggregate == '---' ? 'MAX' : col.aggregate; @@ -142,20 +162,12 @@ export class DesignerQueryDumper { func: aggregate == 'COUNT DISTINCT' ? 'COUNT' : aggregate, argsPrefix: aggregate == 'COUNT DISTINCT' ? 'DISTINCT' : null, alias, - args: [ - { - exprType: 'column', - columnName, - source, - }, - ], + args: [exprCore], }; } else { return { - exprType: 'column', - columnName, + ...exprCore, alias, - source, }; } } @@ -179,24 +191,21 @@ export class DesignerQueryDumper { } } - const topLevelColumns = (this.designer.columns || []).filter(col => - topLevelTables.find(tbl => tbl.designerId == col.designerId) + const topLevelColumns = (this.designer.columns || []).filter( + col => + topLevelTables.find(tbl => tbl.designerId == col.designerId) || (col.isCustomExpression && col.customExpression) ); const selectIsGrouped = !!topLevelColumns.find(x => x.isGrouped || (x.aggregate && x.aggregate != '---')); const outputColumns = topLevelColumns.filter(x => x.isOutput); if (outputColumns.length == 0) { res.selectAll = true; } else { - res.columns = outputColumns.map(col => this.getColumnOutputExpression(col, selectIsGrouped)); + res.columns = outputColumns.map(col => this.getColumnResultField(col, selectIsGrouped)); } const groupedColumns = topLevelColumns.filter(x => x.isGrouped); if (groupedColumns.length > 0) { - res.groupBy = groupedColumns.map(col => ({ - exprType: 'column', - columnName: col.columnName, - source: findQuerySource(this.designer, col.designerId), - })); + res.groupBy = groupedColumns.map(col => this.getColumnExpression(col)); } const orderColumns = _.sortBy( @@ -205,10 +214,8 @@ export class DesignerQueryDumper { ); if (orderColumns.length > 0) { res.orderBy = orderColumns.map(col => ({ - exprType: 'column', + ...this.getColumnExpression(col), direction: col.sortOrder < 0 ? 'DESC' : 'ASC', - columnName: col.columnName, - source: findQuerySource(this.designer, col.designerId), })); } diff --git a/packages/web/src/designer/types.ts b/packages/web/src/designer/types.ts index 000549de2..ee77e1e2f 100644 --- a/packages/web/src/designer/types.ts +++ b/packages/web/src/designer/types.ts @@ -31,6 +31,8 @@ export type DesignerColumnInfo = { sortOrder?: number; filter?: string; groupFilter?: string; + isCustomExpression?: boolean; + customExpression?: string; }; export type DesignerSettings = { diff --git a/packages/web/src/elements/QueryDesignColumns.svelte b/packages/web/src/elements/QueryDesignColumns.svelte index c643d2ec1..de73284df 100644 --- a/packages/web/src/elements/QueryDesignColumns.svelte +++ b/packages/web/src/elements/QueryDesignColumns.svelte @@ -13,8 +13,10 @@ import SelectField from '../forms/SelectField.svelte'; import TextField from '../forms/TextField.svelte'; import InlineButton from '../buttons/InlineButton.svelte'; + import uuidv1 from 'uuid/v1'; import TableControl from './TableControl.svelte'; + import FormStyledButton from '../buttons/FormStyledButton.svelte'; export let value; export let onChange; @@ -35,6 +37,13 @@ })); }; + const addExpressionColumn = () => { + onChange(current => ({ + ...current, + columns: [...(current.columns || []), { isCustomExpression: true, isOutput: true, designerId: uuidv1() }], + })); + }; + $: columns = value?.columns; $: tables = value?.tables; $: hasGroupedColumn = !!(columns || []).find(x => x.isGrouped); @@ -44,7 +53,7 @@ getTableDisplayName(row, tables) }, { fieldName: 'isOutput', header: 'Output', slot: 0 }, { fieldName: 'alias', header: 'Alias', slot: 1 }, @@ -56,6 +65,19 @@ { fieldName: 'actions', header: '', slot: 7 }, ]} > + + {#if row.isCustomExpression} + { + changeColumn({ ...row, customExpression: e.target.value }); + }} + /> + {:else} + {row.columnName} + {/if} + + removeColumn(row)}>Remove + \ No newline at end of file + diff --git a/packages/web/src/elements/TableControl.svelte b/packages/web/src/elements/TableControl.svelte index d22a01524..0647aca69 100644 --- a/packages/web/src/elements/TableControl.svelte +++ b/packages/web/src/elements/TableControl.svelte @@ -92,6 +92,8 @@ {:else if col.slot == 5} {:else if col.slot == 6} {:else if col.slot == 7} + {:else if col.slot == 8} + {:else if col.slot == 9} {/if} {:else} {row[col.fieldName] || ''}