From fc516a929e0c9253fea4999130b2dad71d59968f Mon Sep 17 00:00:00 2001 From: "Gerry.tan" Date: Tue, 7 Jan 2025 11:52:30 +0800 Subject: [PATCH 1/5] perf: Optimize Oracle SQL parsing --- .../oracle/OracleActuator.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/backend/modules/src/main/java/org.jumpserver.chen.modules/oracle/OracleActuator.java b/backend/modules/src/main/java/org.jumpserver.chen.modules/oracle/OracleActuator.java index b068cbc..42680e3 100644 --- a/backend/modules/src/main/java/org.jumpserver.chen.modules/oracle/OracleActuator.java +++ b/backend/modules/src/main/java/org.jumpserver.chen.modules/oracle/OracleActuator.java @@ -1,5 +1,7 @@ package org.jumpserver.chen.modules.oracle; +import com.alibaba.druid.DbType; +import com.alibaba.druid.sql.SQLUtils; import org.jumpserver.chen.framework.datasource.ConnectionManager; import org.jumpserver.chen.framework.datasource.base.BaseSQLActuator; import org.jumpserver.chen.framework.datasource.sql.SQL; @@ -59,4 +61,11 @@ public SQLExecutePlan createPlan(SQL sql) throws SQLException { this.beforeCreatePlan(sql); return super.createPlan(sql); } + + @Override + public List parseSQL(SQL sql) { + return SQLUtils.parseStatements(sql.getSql(), DbType.ali_oracle).stream() + .map(stmt -> SQLUtils.toSQLString(stmt, DbType.ali_oracle)) + .toList(); + } } From 9c56bfe880ea2029bf5d7b81efd5c106adc8135d Mon Sep 17 00:00:00 2001 From: Aaron3S Date: Thu, 9 Jan 2025 18:40:50 +0800 Subject: [PATCH 2/5] =?UTF-8?q?feat:=20=20=E4=BC=98=E5=8C=96=20sql=20?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E5=A4=A7=E5=B0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chen/framework/console/QueryConsole.java | 63 +++++++++++++++++++ .../console/action/QueryConsoleAction.java | 2 + .../Main/Explore/QueryConsole/CodeEditor.vue | 19 +++++- .../Main/Explore/QueryConsole/index.vue | 5 -- 4 files changed, 83 insertions(+), 6 deletions(-) diff --git a/backend/framework/src/main/java/org/jumpserver/chen/framework/console/QueryConsole.java b/backend/framework/src/main/java/org/jumpserver/chen/framework/console/QueryConsole.java index e7e7eeb..4300e4b 100644 --- a/backend/framework/src/main/java/org/jumpserver/chen/framework/console/QueryConsole.java +++ b/backend/framework/src/main/java/org/jumpserver/chen/framework/console/QueryConsole.java @@ -34,6 +34,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; @@ -141,6 +142,7 @@ public void handle(Packet packet) { } } + private void onAction(QueryConsoleAction action) { switch (action.getAction()) { case QueryConsoleAction.ACTION_RUN_SQL -> { @@ -153,6 +155,13 @@ private void onAction(QueryConsoleAction action) { this.getState().setInQuery(false); this.stateManager.commit(); } + case QueryConsoleAction.ACTION_RUN_SQL_CHUNK -> { + this.handleSQLChunk(action); + } + case QueryConsoleAction.ACTION_RUN_SQL_COMPLETE -> { + this.handleSQLComplete(); + } + case QueryConsoleAction.ACTION_RUN_SQL_FILE -> { this.getState().setInQuery(true); this.stateManager.commit(); @@ -177,6 +186,60 @@ private void onAction(QueryConsoleAction action) { } } + private final ConcurrentHashMap sqlChunks = new ConcurrentHashMap<>(); + private CountDownLatch latch; + private int expectedChunks = -1; + + private void handleSQLChunk(QueryConsoleAction action) { + var data = (Map) action.getData(); + var chunk = (String) data.get("chunk"); + var index = (Integer) data.get("index"); + var total = (Integer) data.get("total"); + + synchronized (this) { + if (expectedChunks == -1) { + expectedChunks = total; + latch = new CountDownLatch(total); + } + } + + if (sqlChunks.putIfAbsent(index, chunk) == null) { + latch.countDown(); + } + } + + /** + * 处理分段 SQL 接收完成 + */ + private void handleSQLComplete() { + try { + // 等待所有分段接收完成 + latch.await(); + + // 按照索引顺序合并所有分段 + StringBuilder sqlBuilder = new StringBuilder(); + for (int i = 0; i < expectedChunks; i++) { + sqlBuilder.append(sqlChunks.get(i)); + } + + // 合并完成后清理缓存 + var sql = sqlBuilder.toString(); + sqlChunks.clear(); + expectedChunks = -1; + + // 执行完整 SQL + this.getState().setInQuery(true); + this.stateManager.commit(); + + this.onSQL(sql); + + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } finally { + this.getState().setInQuery(false); + this.stateManager.commit(); + } + } private void onDataViewAction(DataViewAction action) { var dataView = this.dataViews.get(action.getDataView()); diff --git a/backend/framework/src/main/java/org/jumpserver/chen/framework/console/action/QueryConsoleAction.java b/backend/framework/src/main/java/org/jumpserver/chen/framework/console/action/QueryConsoleAction.java index d934f60..05d0a07 100644 --- a/backend/framework/src/main/java/org/jumpserver/chen/framework/console/action/QueryConsoleAction.java +++ b/backend/framework/src/main/java/org/jumpserver/chen/framework/console/action/QueryConsoleAction.java @@ -7,6 +7,8 @@ @EqualsAndHashCode(callSuper = true) public class QueryConsoleAction extends Action { public static final String ACTION_RUN_SQL = "run_sql"; + public static final String ACTION_RUN_SQL_CHUNK = "run_sql_chunk"; + public static final String ACTION_RUN_SQL_COMPLETE = "run_sql_complete"; public static final String ACTION_RUN_SQL_FILE = "run_sql_file"; public static final String ACTION_CANCEL = "cancel"; public static final String ACTION_CHANGE_CURRENT_CONTEXT = "change_current_context"; diff --git a/frontend/src/components/Main/Explore/QueryConsole/CodeEditor.vue b/frontend/src/components/Main/Explore/QueryConsole/CodeEditor.vue index 0aa9c04..b6002d9 100644 --- a/frontend/src/components/Main/Explore/QueryConsole/CodeEditor.vue +++ b/frontend/src/components/Main/Explore/QueryConsole/CodeEditor.vue @@ -253,7 +253,24 @@ export default { }, onRun() { const sql = this.selectionValue || this.statement - this.$emit('action', { action: 'run_sql', data: sql }) + const CHUNK_SIZE = 4096 + + if (sql.length <= CHUNK_SIZE) { + this.$emit('action', { action: 'run_sql', data: sql }) + } else { + const totalChunks = Math.ceil(sql.length / CHUNK_SIZE) + for (let i = 0; i < totalChunks; i++) { + const chunk = sql.slice(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE) + this.$emit('action', { + action: 'run_sql_chunk', + data: { chunk, index: i, total: totalChunks } + }) + } + this.$emit('action', { + action: 'run_sql_complete', + data: { total: totalChunks } + }) + } }, onStop() { this.$emit('action', { action: 'cancel' }) diff --git a/frontend/src/components/Main/Explore/QueryConsole/index.vue b/frontend/src/components/Main/Explore/QueryConsole/index.vue index 4eb15b5..9e1146e 100644 --- a/frontend/src/components/Main/Explore/QueryConsole/index.vue +++ b/frontend/src/components/Main/Explore/QueryConsole/index.vue @@ -8,7 +8,6 @@ :state="state" :subjects="subjects" @action="onEditorAction" - @run="onRunSql" />