Commit 77fdb0d1 authored by godkaikai's avatar godkaikai

语法与逻辑检验

parent 1a3391ad
...@@ -39,6 +39,14 @@ public class StudioController { ...@@ -39,6 +39,14 @@ public class StudioController {
return Result.succeed(jobResult,"执行成功"); return Result.succeed(jobResult,"执行成功");
} }
/**
* 解释Sql
*/
@PostMapping("/explainSql")
public Result explainSql(@RequestBody StudioExecuteDTO studioExecuteDTO) {
return Result.succeed(studioService.explainSql(studioExecuteDTO),"解释成功");
}
/** /**
* 进行DDL操作 * 进行DDL操作
*/ */
......
...@@ -8,6 +8,7 @@ import com.dlink.explainer.ca.TableCANode; ...@@ -8,6 +8,7 @@ import com.dlink.explainer.ca.TableCANode;
import com.dlink.job.JobResult; import com.dlink.job.JobResult;
import com.dlink.result.IResult; import com.dlink.result.IResult;
import com.dlink.result.SelectResult; import com.dlink.result.SelectResult;
import com.dlink.result.SqlExplainResult;
import com.dlink.session.SessionInfo; import com.dlink.session.SessionInfo;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
...@@ -25,6 +26,8 @@ public interface StudioService { ...@@ -25,6 +26,8 @@ public interface StudioService {
IResult executeDDL(StudioDDLDTO studioDDLDTO); IResult executeDDL(StudioDDLDTO studioDDLDTO);
List<SqlExplainResult> explainSql(StudioExecuteDTO studioExecuteDTO);
SelectResult getJobData(String jobId); SelectResult getJobData(String jobId);
SessionInfo createSession(SessionDTO sessionDTO, String createUser); SessionInfo createSession(SessionDTO sessionDTO, String createUser);
......
...@@ -15,6 +15,7 @@ import com.dlink.model.Cluster; ...@@ -15,6 +15,7 @@ import com.dlink.model.Cluster;
import com.dlink.parser.SqlType; import com.dlink.parser.SqlType;
import com.dlink.result.IResult; import com.dlink.result.IResult;
import com.dlink.result.SelectResult; import com.dlink.result.SelectResult;
import com.dlink.result.SqlExplainResult;
import com.dlink.service.ClusterService; import com.dlink.service.ClusterService;
import com.dlink.service.StudioService; import com.dlink.service.StudioService;
import com.dlink.session.SessionConfig; import com.dlink.session.SessionConfig;
...@@ -60,6 +61,16 @@ public class StudioServiceImpl implements StudioService { ...@@ -60,6 +61,16 @@ public class StudioServiceImpl implements StudioService {
return jobManager.executeDDL(studioDDLDTO.getStatement()); return jobManager.executeDDL(studioDDLDTO.getStatement());
} }
@Override
public List<SqlExplainResult> explainSql(StudioExecuteDTO studioExecuteDTO) {
JobConfig config = studioExecuteDTO.getJobConfig();
if(!config.isUseSession()) {
config.setAddress(clusterService.buildEnvironmentAddress(config.isUseRemote(), studioExecuteDTO.getClusterId()));
}
JobManager jobManager = JobManager.build(config);
return jobManager.explainSql(studioExecuteDTO.getStatement());
}
@Override @Override
public SelectResult getJobData(String jobId) { public SelectResult getJobData(String jobId) {
return JobManager.getJobData(jobId); return JobManager.getJobData(jobId);
......
...@@ -135,10 +135,11 @@ ...@@ -135,10 +135,11 @@
<!-- Oracle test dependencies --> <!-- Oracle test dependencies -->
<dependency> <dependency>
<groupId>com.oracle</groupId> <groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc6</artifactId> <artifactId>ojdbc8</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<!--<build> <!--<build>
<plugins> <plugins>
......
...@@ -38,6 +38,10 @@ public class Explainer { ...@@ -38,6 +38,10 @@ public class Explainer {
this.executor = executor; this.executor = executor;
} }
public static Explainer build(Executor executor){
return new Explainer(executor);
}
public List<SqlExplainResult> explainSqlResult(String statement, ExplainDetail... extraDetails) { public List<SqlExplainResult> explainSqlResult(String statement, ExplainDetail... extraDetails) {
String[] sqls = SqlUtil.getStatements(statement); String[] sqls = SqlUtil.getStatements(statement);
List<SqlExplainResult> sqlExplainRecords = new ArrayList<>(); List<SqlExplainResult> sqlExplainRecords = new ArrayList<>();
......
...@@ -6,6 +6,7 @@ import com.dlink.executor.EnvironmentSetting; ...@@ -6,6 +6,7 @@ import com.dlink.executor.EnvironmentSetting;
import com.dlink.executor.Executor; import com.dlink.executor.Executor;
import com.dlink.executor.ExecutorSetting; import com.dlink.executor.ExecutorSetting;
import com.dlink.executor.custom.CustomTableEnvironmentImpl; import com.dlink.executor.custom.CustomTableEnvironmentImpl;
import com.dlink.explainer.Explainer;
import com.dlink.interceptor.FlinkInterceptor; import com.dlink.interceptor.FlinkInterceptor;
import com.dlink.parser.SqlType; import com.dlink.parser.SqlType;
import com.dlink.result.*; import com.dlink.result.*;
...@@ -296,4 +297,9 @@ public class JobManager extends RunTime { ...@@ -296,4 +297,9 @@ public class JobManager extends RunTime {
public static List<SessionInfo> listSession(String createUser){ public static List<SessionInfo> listSession(String createUser){
return SessionPool.filter(createUser); return SessionPool.filter(createUser);
} }
public List<SqlExplainResult> explainSql(String statement){
Explainer explainer = Explainer.build(executor);
return explainer.explainSqlResult(statement);
}
} }
...@@ -22,8 +22,8 @@ ...@@ -22,8 +22,8 @@
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.oracle</groupId> <groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc6</artifactId> <artifactId>ojdbc8</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
......
import {StateType} from "@/pages/FlinkSqlStudio/model";
import {connect} from "umi";
import {Button, Tag, Space, Typography, Modal,} from 'antd';
import {ConsoleSqlOutlined} from "@ant-design/icons";
import ProList from '@ant-design/pro-list';
import React, {useRef, useState} from "react";
const {Paragraph} = Typography;
type ExplainItem = {
index: number;
type: string;
sql: string;
parse: string;
explain: string;
error: string;
parseTrue: string;
explainTrue: string;
explainTime: number;
};
export type StudioExplainProps = {
onCancel: (flag?: boolean) => void;
modalVisible: boolean;
data: Partial<ExplainItem>;
}
const StudioExplain = (props: any) => {
const {
onCancel: handleModalVisible,
modalVisible,
data,
} = props;
const renderFooter = () => {
return (
<>
<Button onClick={() => handleModalVisible(false)}>关闭</Button>
</>
);
};
const renderContent = () => {
return (
<>
<ProList<ExplainItem>
toolBarRender={false}
search={{
filterType: 'light',
}}
rowKey="id"
dataSource={data}
pagination={{
pageSize: 5,
}}
showActions="hover"
metas={{
avatar: {
dataIndex: 'index',
search: false,
},
title: {
dataIndex: 'type',
title: 'type',
render: (_, row) => {
return (
<Space size={0}>
<Tag color="blue" key={row.type}>
<ConsoleSqlOutlined /> {row.type}
</Tag>
</Space>
);
},
},
description: {
search: false,
render: (_, row) => {
return (
<>
{row.sql ?
(<Paragraph ellipsis={{rows: 2, expandable: true, symbol: 'more'}}>
{row.sql}
</Paragraph>) : null
}
{row.explain ?
(<Paragraph>
<pre style={{height: '80px'}}>
{row.explain}
</pre>
</Paragraph>) : null
}
{row.error ?
(<Paragraph>
<pre style={{height: '80px'}}>
{row.error}
</pre>
</Paragraph>) : null
}
</>
)
}
},
subTitle: {
render: (_, row) => {
return (
<Space size={0}>
{row.parseTrue ?
(<Tag color="#44b549">语法正确</Tag>) :
(<Tag color="#ff4d4f">语法有误</Tag>)}
{row.explainTrue ?
(<Tag color="#108ee9">逻辑正确</Tag>) :
(<Tag color="#ff4d4f">逻辑有误</Tag>)}
{row.explainTime}
</Space>
);
},
search: false,
},
}}
options={{
search: false,
setting: false
}}
/>
</>)
};
return (
<Modal
width={800}
destroyOnClose
title="FlinkSql 语法和逻辑检查"
visible={modalVisible}
footer={renderFooter()}
onCancel={() => handleModalVisible()}
>
{renderContent()}
</Modal>
);
};
export default connect(({Studio}: {Studio: StateType}) => ({
current: Studio.current,
}))(StudioExplain);
...@@ -82,7 +82,10 @@ const StudioPreview = (props:any) => { ...@@ -82,7 +82,10 @@ const StudioPreview = (props:any) => {
return ( return (
<div style={{width: '100%'}}> <div style={{width: '100%'}}>
{result&&result.jobId&&!result.isDestroyed&&result.rowData&&result.columns? {result&&result.jobId&&!result.isDestroyed&&result.rowData&&result.columns?
(<ProTable dataSource={result.rowData} columns={getColumns(result.columns)} search={false} (<ProTable dataSource={result.rowData} columns={getColumns(result.columns)} search={false} toolBarRender={false}
pagination={{
pageSize: 5,
}}
/>):(<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />)} />):(<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />)}
</div> </div>
); );
......
...@@ -87,8 +87,8 @@ const StudioProcess = (props: any) => { ...@@ -87,8 +87,8 @@ const StudioProcess = (props: any) => {
{row.tasks.created>0?(<Tooltip title="CREATED"><Tag color="#666">{row.tasks.created}</Tag></Tooltip>):''} {row.tasks.created>0?(<Tooltip title="CREATED"><Tag color="#666">{row.tasks.created}</Tag></Tooltip>):''}
{row.tasks.deploying>0?(<Tooltip title="DEPLOYING"><Tag color="#666">{row.tasks.deploying}</Tag></Tooltip>):''} {row.tasks.deploying>0?(<Tooltip title="DEPLOYING"><Tag color="#666">{row.tasks.deploying}</Tag></Tooltip>):''}
{row.tasks.running>0?(<Tooltip title="RUNNING"><Tag color="#44b549">{row.tasks.running}</Tag></Tooltip>):''} {row.tasks.running>0?(<Tooltip title="RUNNING"><Tag color="#44b549">{row.tasks.running}</Tag></Tooltip>):''}
{row.tasks.failed>0?(<Tooltip title="FAILED"><Tag color="#666">{row.tasks.failed}</Tag></Tooltip>):''} {row.tasks.failed>0?(<Tooltip title="FAILED"><Tag color="#ff4d4f">{row.tasks.failed}</Tag></Tooltip>):''}
{row.tasks.finished>0?(<Tooltip title="CREATED"><Tag color="#108ee9">{row.tasks.finished}</Tag></Tooltip>):''} {row.tasks.finished>0?(<Tooltip title="FINISHED"><Tag color="#108ee9">{row.tasks.finished}</Tag></Tooltip>):''}
{row.tasks.reconciling>0?(<Tooltip title="RECONCILING"><Tag color="#666">{row.tasks.reconciling}</Tag></Tooltip>):''} {row.tasks.reconciling>0?(<Tooltip title="RECONCILING"><Tag color="#666">{row.tasks.reconciling}</Tag></Tooltip>):''}
{row.tasks.scheduled>0?(<Tooltip title="SCHEDULED"><Tag color="#666">{row.tasks.scheduled}</Tag></Tooltip>):''} {row.tasks.scheduled>0?(<Tooltip title="SCHEDULED"><Tag color="#666">{row.tasks.scheduled}</Tag></Tooltip>):''}
{row.tasks.canceling>0?(<Tooltip title="CANCELING"><Tag color="#feb72b">{row.tasks.canceling}</Tag></Tooltip>):''} {row.tasks.canceling>0?(<Tooltip title="CANCELING"><Tag color="#feb72b">{row.tasks.canceling}</Tag></Tooltip>):''}
...@@ -189,6 +189,9 @@ const StudioProcess = (props: any) => { ...@@ -189,6 +189,9 @@ const StudioProcess = (props: any) => {
</Space> </Space>
{jobsData.length > 0 ? {jobsData.length > 0 ?
(<ProTable dataSource={jobsData} columns={getColumns()} size="small" search={false} toolBarRender={false} (<ProTable dataSource={jobsData} columns={getColumns()} size="small" search={false} toolBarRender={false}
pagination={{
pageSize: 5,
}}
/>) : (<Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/>)} />) : (<Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/>)}
</div> </div>
); );
......
...@@ -7,6 +7,7 @@ import {connect} from "umi"; ...@@ -7,6 +7,7 @@ import {connect} from "umi";
import {useState} from "react"; import {useState} from "react";
import styles from "./index.less"; import styles from "./index.less";
import { import {
SearchOutlined,
ReloadOutlined, ReloadOutlined,
DownOutlined, DownOutlined,
DeleteOutlined, DeleteOutlined,
...@@ -23,7 +24,6 @@ import { ...@@ -23,7 +24,6 @@ import {
ModalForm, ModalForm,
} from '@ant-design/pro-form'; } from '@ant-design/pro-form';
import ProDescriptions from '@ant-design/pro-descriptions'; import ProDescriptions from '@ant-design/pro-descriptions';
import {getData, handleAddOrUpdate} from "@/components/Common/crud";
import SessionForm from "@/components/Studio/StudioLeftTool/StudioConnector/components/SessionForm"; import SessionForm from "@/components/Studio/StudioLeftTool/StudioConnector/components/SessionForm";
const StudioConnector = (props: any) => { const StudioConnector = (props: any) => {
......
...@@ -11,10 +11,12 @@ import Button from "antd/es/button/button"; ...@@ -11,10 +11,12 @@ import Button from "antd/es/button/button";
import Breadcrumb from "antd/es/breadcrumb/Breadcrumb"; import Breadcrumb from "antd/es/breadcrumb/Breadcrumb";
import {StateType} from "@/pages/FlinkSqlStudio/model"; import {StateType} from "@/pages/FlinkSqlStudio/model";
import {connect} from "umi"; import {connect} from "umi";
import {postDataArray} from "@/components/Common/crud"; import {handleAddOrUpdate, postDataArray} from "@/components/Common/crud";
import {executeSql} from "@/pages/FlinkSqlStudio/service"; import {executeSql, explainSql} from "@/pages/FlinkSqlStudio/service";
import StudioHelp from "./StudioHelp"; import StudioHelp from "./StudioHelp";
import {showTables} from "@/components/Studio/StudioEvent/DDL"; import {showCluster, showTables} from "@/components/Studio/StudioEvent/DDL";
import {useState} from "react";
import StudioExplain from "../StudioConsole/StudioExplain";
const menu = ( const menu = (
<Menu> <Menu>
...@@ -26,6 +28,8 @@ const menu = ( ...@@ -26,6 +28,8 @@ const menu = (
const StudioMenu = (props: any) => { const StudioMenu = (props: any) => {
const {tabs, current, currentPath, form, refs, dispatch, currentSession} = props; const {tabs, current, currentPath, form, refs, dispatch, currentSession} = props;
const [modalVisible, handleModalVisible] = useState<boolean>(false);
const [explainData, setExplainData] = useState([]);
const execute = () => { const execute = () => {
let selectsql = null; let selectsql = null;
...@@ -127,10 +131,48 @@ const StudioMenu = (props: any) => { ...@@ -127,10 +131,48 @@ const StudioMenu = (props: any) => {
}); });
}; };
const onCheckSql = ()=>{
let selectsql = null;
if (current.monaco.current) {
let selection = current.monaco.current.editor.getSelection();
selectsql = current.monaco.current.editor.getModel().getValueInRange(selection);
}
if (selectsql == null || selectsql == '') {
selectsql = current.value;
}
let useSession = !!currentSession.session;
let param = {
useSession: useSession,
session: currentSession.session,
useRemote: current.task.useRemote,
clusterId: current.task.clusterId,
useResult: current.task.useResult,
maxRowNum: current.task.maxRowNum,
statement: selectsql,
fragment: current.task.fragment,
jobName: current.task.jobName,
parallelism: current.task.parallelism,
checkPoint: current.task.checkPoint,
savePointPath: current.task.savePointPath,
};
const taskKey = (Math.random() * 1000) + '';
notification.success({
message: `新任务【${param.jobName}】正在检查`,
description: param.statement.substring(0, 40) + '...',
duration: null,
key: taskKey,
icon: <SmileOutlined style={{color: '#108ee9'}}/>,
});
const result = explainSql(param);
handleModalVisible(true);
result.then(res => {
notification.close(taskKey);
setExplainData(res.datas);
})
};
const saveSqlAndSettingToTask = async () => { const saveSqlAndSettingToTask = async () => {
const fieldsValue = await form.validateFields(); const fieldsValue = await form.validateFields();
// console.log(fieldsValue);
// return;
if (current.task) { if (current.task) {
let task = { let task = {
...current.task, ...current.task,
...@@ -243,10 +285,13 @@ const StudioMenu = (props: any) => { ...@@ -243,10 +285,13 @@ const StudioMenu = (props: any) => {
/> />
</Tooltip> </Tooltip>
<Divider type="vertical"/> <Divider type="vertical"/>
<Button <Tooltip title="检查当前的 FlinkSql">
type="text" <Button
icon={<SafetyCertificateTwoTone twoToneColor="#ddd"/>} type="text"
/> icon={<SafetyCertificateTwoTone />}
onClick={onCheckSql}
/>
</Tooltip>
<Button <Button
type="text" type="text"
icon={<FlagTwoTone twoToneColor="#ddd"/>} icon={<FlagTwoTone twoToneColor="#ddd"/>}
...@@ -305,6 +350,14 @@ const StudioMenu = (props: any) => { ...@@ -305,6 +350,14 @@ const StudioMenu = (props: any) => {
</Col> </Col>
</Row> </Row>
</Col> </Col>
<StudioExplain
onCancel={() => {
handleModalVisible(false);
setExplainData([]);
}}
modalVisible={modalVisible}
data={explainData}
/>
</Row> </Row>
); );
}; };
......
...@@ -19,6 +19,15 @@ export async function executeDDL(params: StudioParam) { ...@@ -19,6 +19,15 @@ export async function executeDDL(params: StudioParam) {
}); });
} }
export async function explainSql(params: StudioParam) {
return request<API.Result>('/api/studio/explainSql', {
method: 'POST',
data: {
...params,
},
});
}
export async function getJobData(jobId:string) { export async function getJobData(jobId:string) {
return request<API.Result>('/api/studio/getJobData', { return request<API.Result>('/api/studio/getJobData', {
method: 'GET', method: 'GET',
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
<guava.version>21.0</guava.version> <guava.version>21.0</guava.version>
<slf4j.version>1.7.30</slf4j.version> <slf4j.version>1.7.30</slf4j.version>
<mysql-connector-java.version>8.0.22</mysql-connector-java.version> <mysql-connector-java.version>8.0.22</mysql-connector-java.version>
<ojdbc6.version>11.2.0.3</ojdbc6.version> <ojdbc8.version>12.2.0.1</ojdbc8.version>
<clickhouse.version>0.2.6</clickhouse.version> <clickhouse.version>0.2.6</clickhouse.version>
<postgresql.version>42.2.14</postgresql.version> <postgresql.version>42.2.14</postgresql.version>
<banner.version>1.0.2</banner.version> <banner.version>1.0.2</banner.version>
...@@ -122,9 +122,9 @@ ...@@ -122,9 +122,9 @@
<version>${mysql-connector-java.version}</version> <version>${mysql-connector-java.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.oracle</groupId> <groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc6</artifactId> <artifactId>ojdbc8</artifactId>
<version>${ojdbc6.version}</version> <version>${ojdbc8.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ru.yandex.clickhouse</groupId> <groupId>ru.yandex.clickhouse</groupId>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment