diff --git a/headless/api/src/main/java/com/tencent/supersonic/headless/api/pojo/request/QueryStructReq.java b/headless/api/src/main/java/com/tencent/supersonic/headless/api/pojo/request/QueryStructReq.java index 55b3bbbf3..5a6c3816e 100644 --- a/headless/api/src/main/java/com/tencent/supersonic/headless/api/pojo/request/QueryStructReq.java +++ b/headless/api/src/main/java/com/tencent/supersonic/headless/api/pojo/request/QueryStructReq.java @@ -170,6 +170,9 @@ public class QueryStructReq extends SemanticQueryReq { // 5. Set the limit clause plainSelect.setLimit(buildLimit(queryStructReq)); + // 6. Set having clause + plainSelect.setHaving(buildHavingClause(queryStructReq)); + select.setSelect(plainSelect); // 6. Set where clause @@ -239,7 +242,8 @@ public class QueryStructReq extends SemanticQueryReq { private GroupByElement buildGroupByElement(QueryStructReq queryStructReq) { List groups = queryStructReq.getGroups(); - if (!CollectionUtils.isEmpty(groups) && !queryStructReq.getAggregators().isEmpty()) { + if ((!CollectionUtils.isEmpty(groups) && !queryStructReq.getAggregators().isEmpty()) + || !queryStructReq.getMetricFilters().isEmpty()) { GroupByElement groupByElement = new GroupByElement(); for (String group : groups) { groupByElement.addGroupByExpression(new Column(group)); @@ -289,4 +293,23 @@ public class QueryStructReq extends SemanticQueryReq { } return Constants.TABLE_PREFIX + StringUtils.join(modelIds, "_"); } + + public Expression buildHavingClause(QueryStructReq queryStructReq) { + if (queryStructReq.getMetricFilters().isEmpty()) { + return null; + } + + List filters = queryStructReq.getMetricFilters(); + SqlFilterUtils sqlFilterUtils = ContextUtils.getBean(SqlFilterUtils.class); + String havingClause = sqlFilterUtils.getWhereClause(filters, false); + if (StringUtils.isNotBlank(havingClause)) { + try { + return CCJSqlParserUtil.parseCondExpression(havingClause); + } catch (JSQLParserException e) { + log.error("Failed to parse having clause", e); + } + } + return null; + } + } diff --git a/headless/server/src/test/java/com/tencent/supersonic/headless/server/utils/SqlVariableParseUtilsTest.java b/headless/server/src/test/java/com/tencent/supersonic/headless/server/utils/SqlVariableParseUtilsTest.java index b5ab31878..36f799154 100644 --- a/headless/server/src/test/java/com/tencent/supersonic/headless/server/utils/SqlVariableParseUtilsTest.java +++ b/headless/server/src/test/java/com/tencent/supersonic/headless/server/utils/SqlVariableParseUtilsTest.java @@ -34,6 +34,18 @@ public class SqlVariableParseUtilsTest { Assertions.assertEquals(expectedSql, actualSql); } + @Test + void testParseSql_if() { + String sql = "select * from t_$interval$ where id = $id$ $if(name)$and name = $name$$endif$"; + List variables = Lists.newArrayList(mockNumSqlVariable(), + mockExprSqlVariable(), mockStrSqlVariable()); + List params = + Lists.newArrayList(mockIdParam(), mockNameParam(), mockIntervalParam()); + String actualSql = SqlVariableParseUtils.parse(sql, variables, params); + String expectedSql = "select * from t_wk where id = 2 and name = 'alice'"; + Assertions.assertEquals(expectedSql, actualSql); + } + private SqlVariable mockNumSqlVariable() { return mockSqlVariable("id", VariableValueType.NUMBER, 1); } diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/SqlDetail.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/SqlDetail.tsx index eeee90f6e..e14193f52 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/SqlDetail.tsx +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Datasource/components/SqlDetail.tsx @@ -18,7 +18,7 @@ import { isFunction } from 'lodash'; import FullScreen from '@/components/FullScreen'; import SqlEditor from '@/components/SqlEditor'; import type { TaskResultItem, TaskResultColumn } from '../data'; -import { executeSql, listColumnsBySql } from '@/pages/SemanticModel/service'; +import { executeSql } from '@/pages/SemanticModel/service'; import SqlParams from './SqlParams'; import styles from '../style.less'; @@ -248,19 +248,12 @@ const SqlDetail: React.FC = ({ id: currentDatabaseItem.key, sqlVariables: sqlParams, }); - - const { code: getColumnCode, data: getColumnData } = await listColumnsBySql({ - sql: value, - databaseId: currentDatabaseItem.key, - }); - setResultTableLoading(false); - if (code === 200 && getColumnCode === 200) { - const columnData = getColumnData.map((item) => { + if (code === 200) { + const columnData = (data.columns || []).map((item: any) => { return { ...item, - nameEn: item.columnName, - type: item.dataType, + columnName: item.nameEn, }; }); fetchTaskResult(data, columnData); diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/service.ts b/webapp/packages/supersonic-fe/src/pages/SemanticModel/service.ts index e2cee2db9..34582ab4d 100644 --- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/service.ts +++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/service.ts @@ -373,12 +373,6 @@ export async function executeSql(params: ExcuteSqlParams) { return request.post(`${process.env.API_BASE_URL}database/executeSql`, { data }); } -export async function listColumnsBySql(data: { databaseId: number; sql: string }) { - return request.post(`${process.env.API_BASE_URL}database/listColumnsBySql`, { - data, - }); -} - export function getCatalogs(dbId: number): Promise { return request(`${process.env.API_BASE_URL}database/getCatalogs`, { method: 'GET', @@ -409,7 +403,12 @@ export function getTables(databaseId: number, catalog: string, dbName: string): }); } -export function getColumns(databaseId: number, catalog: string, dbName: string, tableName: string): Promise { +export function getColumns( + databaseId: number, + catalog: string, + dbName: string, + tableName: string, +): Promise { return request(`${process.env.API_BASE_URL}database/getColumnsByName`, { method: 'GET', params: {