mirror of
https://github.com/CodePhiliaX/Chat2DB.git
synced 2026-03-13 09:02:05 +08:00
merge dev
This commit is contained in:
2
.github/workflows/release_test.yml
vendored
2
.github/workflows/release_test.yml
vendored
@@ -7,6 +7,8 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- "release_test"
|
||||
- "release_test_2"
|
||||
- "release_test_3"
|
||||
|
||||
# Workflow's jobs
|
||||
# 一共需要3台电脑运行
|
||||
|
||||
69
CHANGELOG.md
69
CHANGELOG.md
@@ -1,59 +1,68 @@
|
||||
# 2.0.14
|
||||
|
||||
## 🐞 Bug Fixes
|
||||
|
||||
- Fix the issue of 'Oracle' query 'Blob' reporting errors
|
||||
- Modify the paging logic and fix some SQL queries that cannot be queried
|
||||
|
||||
## 🐞 问题修复
|
||||
|
||||
- 修复 `Oracle` 查询 `Blob` 报错的问题
|
||||
- 修改分页逻辑,修复部分 SQL 无法查询
|
||||
|
||||
# 2.0.13
|
||||
|
||||
## ⭐ New Features
|
||||
|
||||
## 🐞 Bug Fixes
|
||||
- Fixed a bug where sql formatting was not selected
|
||||
- Fixed open view lag issue
|
||||
|
||||
- Fixed a bug where sql formatting was not selected
|
||||
- Fixed open view lag issue
|
||||
- Solve the white screen problem of connected non-relational databases (non-relational databases are not supported)
|
||||
|
||||
## ⭐ 新特性
|
||||
|
||||
|
||||
|
||||
## 🐞 问题修复
|
||||
- 修复不选中sql格式化的bug
|
||||
|
||||
- 修复不选中 sql 格式化的 bug
|
||||
- 修复打开视图卡顿问题
|
||||
- 解决已连接的非关系型数据库打开白屏问题(暂不支持非关系性数据库)
|
||||
|
||||
|
||||
# 2.0.12
|
||||
|
||||
## ⭐ New Features
|
||||
|
||||
- 🔥Supports viewing views, functions, triggers, and procedures
|
||||
- Support selected sql formatting
|
||||
- 🔥Supports viewing views, functions, triggers, and procedures
|
||||
- Support selected sql formatting
|
||||
- Added new dark themes
|
||||
|
||||
## 🐞 Bug Fixes
|
||||
|
||||
- Fixed sql formatting failure issue
|
||||
- Fixed an issue where locally stored theme colors and background colors are incompatible with the new version, causing page crashes
|
||||
- Fixed an issue where locally stored theme colors and background colors are incompatible with the new version, causing
|
||||
page crashes
|
||||
- Logs desensitize sensitive data
|
||||
- Fix the issue of 'CLOB' not displaying specific content [Issue #440](https://github.com/chat2db/Chat2DB/issues/440)
|
||||
- Fix the issue of 'CLOB' not displaying specific content [Issue #440](https://github.com/chat2db/Chat2DB/issues/440)
|
||||
- Fix the problem that non-Select does not display query results
|
||||
- Fix the problem that Oracle cannot query without schema
|
||||
- Fix the problem of special type of SQL execution error reporting
|
||||
- Fix the problem that the test link is successful, but the error is reported when saving the link
|
||||
|
||||
|
||||
|
||||
|
||||
## ⭐ 新特性
|
||||
|
||||
- 🔥支持查看视图、函数、触发器、存储过程
|
||||
- 支持选中sql格式化
|
||||
- 🔥 支持查看视图、函数、触发器、存储过程
|
||||
- 支持选中 sql 格式化
|
||||
- 增加新的暗色主题
|
||||
|
||||
## 🐞 问题修复
|
||||
|
||||
- 修复sql格式化会失败问题
|
||||
- 修复 sql 格式化会失败问题
|
||||
- 修复本地存储的主题色、背景色与新版本不兼容时会导致页面崩溃问题
|
||||
- 日志对敏感数据进行脱敏
|
||||
- 修复 `CLOB` 不展示具体内容的问题 [Issue #440](https://github.com/chat2db/Chat2DB/issues/440)
|
||||
- 修复非Select不展示查询结果的问题
|
||||
- 修复Oracle不带schema无法查询的问题
|
||||
- 修复特殊类型的SQL执行报错的问题
|
||||
- 修复 `CLOB` 不展示具体内容的问题 [Issue #440](https://github.com/chat2db/Chat2DB/issues/440)
|
||||
- 修复非 Select 不展示查询结果的问题
|
||||
- 修复 Oracle 不带 schema 无法查询的问题
|
||||
- 修复特殊类型的 SQL 执行报错的问题
|
||||
- 修复测试链接成功,但保存链接报错的问题
|
||||
|
||||
# 2.0.11
|
||||
@@ -81,11 +90,11 @@
|
||||
|
||||
## 🐞 问题修复
|
||||
|
||||
- 新建、开打console时激活最新操作的console、记录最后一次使用的console
|
||||
- edge等浏览器复制功能无法正常使用
|
||||
- table搜索后导出ddl报错
|
||||
- 新建、开打 console 时激活最新操作的 console、记录最后一次使用的 console
|
||||
- edge 等浏览器复制功能无法正常使用
|
||||
- table 搜索后导出 ddl 报错
|
||||
- 增加表注释以及列字段类型和注释
|
||||
- 当数据源添加了database默认选择第一个database
|
||||
- 当数据源添加了 database 默认选择第一个 database
|
||||
|
||||
# 2.0.9
|
||||
|
||||
@@ -95,18 +104,18 @@
|
||||
|
||||
## 🐞 问题修复
|
||||
|
||||
- 修复windows闪退的问题
|
||||
- 修复 windows 闪退的问题
|
||||
|
||||
# 2.0.8
|
||||
|
||||
## 🐞 Bug Fixes
|
||||
|
||||
- Repair the Scientific notation in some databases [Issue #378](https://github.com/chat2db/Chat2DB/issues/378)
|
||||
- Repair the Scientific notation in some databases [Issue #378](https://github.com/chat2db/Chat2DB/issues/378)
|
||||
- Fix some cases where data is not displayed
|
||||
|
||||
## 🐞 问题修复
|
||||
|
||||
- 修复部分数据库出现科学计数法的情况 [Issue #378](https://github.com/chat2db/Chat2DB/issues/378)
|
||||
- 修复部分数据库出现科学计数法的情况 [Issue #378](https://github.com/chat2db/Chat2DB/issues/378)
|
||||
- 修复部分情况数据不展示
|
||||
|
||||
# 2.0.7
|
||||
@@ -125,18 +134,18 @@
|
||||
|
||||
## 🐞 问题修复
|
||||
|
||||
- 修复ai配置 [Issue #346](https://github.com/chat2db/Chat2DB/issues/346)
|
||||
- 修复 ai 配置 [Issue #346](https://github.com/chat2db/Chat2DB/issues/346)
|
||||
|
||||
# 2.0.6
|
||||
|
||||
## 🐞 Bug Fixes
|
||||
|
||||
- Fixed: When there are too many tables under the selected library, the "New Console" button at the bottom
|
||||
disappears [Issue #314](https://github.com/chat2db/Chat2DB/issues/314)
|
||||
disappears [Issue #314](https://github.com/chat2db/Chat2DB/issues/314)
|
||||
|
||||
## 🐞 问题修复
|
||||
|
||||
- Fixed: 当选择的库下面表过多时最下面的“新建控制台”按钮消失 [Issue #314](https://github.com/chat2db/Chat2DB/issues/314)
|
||||
- Fixed: 当选择的库下面表过多时最下面的“新建控制台”按钮消失 [Issue #314](https://github.com/chat2db/Chat2DB/issues/314)
|
||||
|
||||
# 2.0.5
|
||||
|
||||
|
||||
@@ -192,6 +192,8 @@ Follow our WeChat public account.
|
||||
|
||||
<a><img src="https://github.com/chat2db/Chat2DB/assets/22975773/e4239d29-1426-4361-bf57-f1b0b67d1281" width="40%"/></a>
|
||||
|
||||
Click and join <a href="https://discord.gg/dqae8nsC">discord server</a>
|
||||
|
||||
## ❤️ Acknowledgements
|
||||
|
||||
Thanks to all the students who contributed to Chat2DB~
|
||||
|
||||
18
README_CN.md
18
README_CN.md
@@ -14,21 +14,15 @@
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
<p align="center"><b>分享 Chat2DB </b></p>
|
||||
<p align="center">
|
||||
<a href="https://twitter.com/intent/tweet?text=Chat2DB-An%20intelligent%20and%20versatile%20general-purpose%20SQL%20client%20and%20reporting%20tool%20for%20databases%20which%20integrates%20ChatGPT%20capabilities.&url=https://github.com/chat2db/Chat2DB&hashtags=ChatGPT,AGI,SQL%20Client,Reporting%20tool" target="blank" > <img src="https://img.shields.io/twitter/follow/_Chat2DB?label=Share Repo on Twitter&style=social" alt=""/> </a>
|
||||
<a href="https://t.me/share/url?text=Chat2DB-An%20intelligent%20and%20versatile%20general-purpose%20SQL%20client%20and%20reporting%20tool%20for%20databases%20which%20integrates%20ChatGPT%20capabilities.&url=https://github.com/chat2db/Chat2DB" target="_blank"><img src="https://img.shields.io/twitter/url?label=Telegram&logo=Telegram&style=social&url=https://github.com/chat2db/Chat2DB" alt="Share on Telegram"/></a>
|
||||
<a href="https://api.whatsapp.com/send?text=Chat2DB-An%20intelligent%20and%20versatile%20general-purpose%20SQL%20client%20and%20reporting%20tool%20for%20databases%20which%20integrates%20ChatGPT%20capabilities.%20https://github.com/chat2db/Chat2DB"><img src="https://img.shields.io/twitter/url?label=whatsapp&logo=whatsapp&style=social&url=https://github.com/chat2db/Chat2DB" /></a>
|
||||
<a href="https://www.reddit.com/submit?url=https://github.com/chat2db/Chat2DB&title=Chat2DB-An%20intelligent%20and%20versatile%20general-purpose%20SQL%20client%20and%20reporting%20tool%20for%20databases%20which%20integrates%20ChatGPT%20capabilities." target="blank"><img src="https://img.shields.io/twitter/url?label=Reddit&logo=Reddit&style=social&url=https://github.com/chat2db/Chat2DB" alt="Share on Reddit"/></a>
|
||||
<a href="mailto:?subject=Check%20this%20GitHub%20repository%20out.&body=Chat2DB-An%20intelligent%20and%20versatile%20general-purpose%20SQL%20client%20and%20reporting%20tool%20for%20databases%20which%20integrates%20ChatGPT%20capabilities.%3A%0Ahttps://github.com/chat2db/Chat2DB" target="_blank"><img src="https://img.shields.io/twitter/url?label=Gmail&logo=Gmail&style=social&url=https://github.com/chat2db/Chat2DB"/></a>
|
||||
</p>
|
||||
|
||||
**许可说明**: Chat2DB 开源内容仅供个人免费使用,如想将该项目用于商业用途,请先联系该项目作者。
|
||||
|
||||
Languages: 中文 [English](README.md)
|
||||
|
||||
Languages: 中文 [English](README.md)
|
||||
|
||||
如果觉得 Chat2DB 对您有帮助的话,请帮忙<a target="_blank" href='https://github.com/chat2db/Chat2db'><img src="https://img.shields.io/github/stars/chat2db/Chat2DB.svg?style=flat-square&label=Stars&logo=github" alt="github star"/></a>
|
||||
的右上角点个⭐ Star 和 Fork,您的支持是 Chat2DB 变得更好最大的动力
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
## 案例视频
|
||||
|
||||
https://github.com/chat2db/Chat2DB/assets/22975773/b58db908-5768-4a71-aa30-135d202e505f
|
||||
|
||||
@@ -34,7 +34,7 @@ public class DMMetaData extends DefaultMetaService implements MetaData {
|
||||
}
|
||||
|
||||
private static String ROUTINES_SQL
|
||||
= "SELECT OWNER, NAME, TEXT FROM ALL_SOURCE WHERE TYPE = '%s' AND NAME = '%s' AND NAME = '%s' ORDER BY LINE";
|
||||
= "SELECT OWNER, NAME, TEXT FROM ALL_SOURCE WHERE TYPE = '%s' AND OWNER = '%s' AND NAME = '%s' ORDER BY LINE";
|
||||
|
||||
@Override
|
||||
public Function function(Connection connection, @NotEmpty String databaseName, String schemaName,
|
||||
|
||||
@@ -68,6 +68,10 @@ public class SqlServerMetaData extends DefaultMetaService implements MetaData {
|
||||
= "SELECT type_desc, OBJECT_NAME(object_id) AS FunctionName, OBJECT_DEFINITION(object_id) AS "
|
||||
+ "definition FROM sys.objects WHERE type_desc IN(%s) and name = '%s' ;";
|
||||
|
||||
|
||||
private static String OBJECT_SQL
|
||||
= "SELECT name FROM sys.objects WHERE type = '%s' and SCHEMA_ID = SCHEMA_ID('%s');";
|
||||
|
||||
@Override
|
||||
public Function function(Connection connection, @NotEmpty String databaseName, String schemaName,
|
||||
String functionName) {
|
||||
@@ -87,8 +91,22 @@ public class SqlServerMetaData extends DefaultMetaService implements MetaData {
|
||||
|
||||
@Override
|
||||
public List<Function> functions(Connection connection, String databaseName, String schemaName) {
|
||||
List<Function> functions = SQLExecutor.getInstance().functions(connection, databaseName, schemaName);
|
||||
return functions.stream().map(function -> removeVersion(function)).collect(Collectors.toList());
|
||||
List<Function> functions = new ArrayList<>();
|
||||
// List<Function> functions = SQLExecutor.getInstance().functions(connection, databaseName, schemaName);
|
||||
// return functions.stream().map(function -> removeVersion(function)).collect(Collectors.toList());
|
||||
String sql = String.format(OBJECT_SQL,"FN", schemaName);
|
||||
return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
|
||||
while (resultSet.next()) {
|
||||
Function function = new Function();
|
||||
function.setDatabaseName(databaseName);
|
||||
function.setSchemaName(schemaName);
|
||||
if (resultSet.next()) {
|
||||
function.setFunctionName(resultSet.getString("name"));
|
||||
}
|
||||
functions.add(function);
|
||||
}
|
||||
return functions;
|
||||
});
|
||||
}
|
||||
|
||||
private Function removeVersion(Function function) {
|
||||
@@ -103,8 +121,20 @@ public class SqlServerMetaData extends DefaultMetaService implements MetaData {
|
||||
|
||||
@Override
|
||||
public List<Procedure> procedures(Connection connection, String databaseName, String schemaName) {
|
||||
List<Procedure> procedures = SQLExecutor.getInstance().procedures(connection, databaseName, schemaName);
|
||||
return procedures.stream().map(procedure -> removeVersion(procedure)).collect(Collectors.toList());
|
||||
List<Procedure> procedures = new ArrayList<>();
|
||||
String sql = String.format(OBJECT_SQL,"P", schemaName);
|
||||
return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
|
||||
while (resultSet.next()) {
|
||||
Procedure procedure = new Procedure();
|
||||
procedure.setDatabaseName(databaseName);
|
||||
procedure.setSchemaName(schemaName);
|
||||
if (resultSet.next()) {
|
||||
procedure.setProcedureName(resultSet.getString("name"));
|
||||
}
|
||||
procedures.add(procedure);
|
||||
}
|
||||
return procedures;
|
||||
});
|
||||
}
|
||||
|
||||
private Procedure removeVersion(Procedure procedure) {
|
||||
|
||||
@@ -87,12 +87,42 @@ public class DatabaseServiceImpl implements DatabaseService {
|
||||
(key) -> param.isRefresh(), (key) -> {
|
||||
Connection connection = param.getConnection() == null ? Chat2DBContext.getConnection()
|
||||
: param.getConnection();
|
||||
MetaData metaData = Chat2DBContext.getMetaData();
|
||||
return metaData.schemas(connection, param.getDataBaseName());
|
||||
return getSchemaList(param.getDataBaseName(), connection);
|
||||
});
|
||||
return ListResult.of(schemas);
|
||||
}
|
||||
|
||||
|
||||
private List<Schema> getSchemaList(String databaseName, Connection connection) {
|
||||
MetaData metaData = Chat2DBContext.getMetaData();
|
||||
List<Schema> schemas = metaData.schemas(connection,databaseName);
|
||||
sortSchema(schemas,connection);
|
||||
return schemas;
|
||||
}
|
||||
|
||||
private void sortSchema(List<Schema> schemas,Connection connection) {
|
||||
if (CollectionUtils.isEmpty(schemas)) {
|
||||
return;
|
||||
}
|
||||
String ulr = null;
|
||||
try {
|
||||
ulr = connection.getMetaData().getURL();
|
||||
} catch (SQLException e) {
|
||||
log.error("get url error", e);
|
||||
}
|
||||
// If the database name contains the name of the current database, the current database is placed in the first place
|
||||
int num = -1;
|
||||
for (int i = 0; i < schemas.size(); i++) {
|
||||
if (StringUtils.isNotBlank(ulr) && ulr.contains(schemas.get(i).getName())) {
|
||||
num = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (num != -1 && num != 0) {
|
||||
Collections.swap(schemas, num, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataResult<MetaSchema> queryDatabaseSchema(MetaDataQueryParam param) {
|
||||
MetaSchema metaSchema = new MetaSchema();
|
||||
|
||||
@@ -34,7 +34,6 @@ import ai.chat2db.spi.util.SqlUtils;
|
||||
import com.google.common.collect.Lists;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -67,7 +66,7 @@ public class DlTemplateServiceImpl implements DlTemplateService {
|
||||
ListResult<ExecuteResult> listResult = ListResult.of(result);
|
||||
// 执行sql
|
||||
for (String originalSql : sqlList) {
|
||||
ExecuteResult executeResult = executeSQL(originalSql,dbType,param);
|
||||
ExecuteResult executeResult = executeSQL(originalSql, dbType, param);
|
||||
result.add(executeResult);
|
||||
if (!executeResult.getSuccess()) {
|
||||
listResult.setSuccess(false);
|
||||
@@ -78,53 +77,44 @@ public class DlTemplateServiceImpl implements DlTemplateService {
|
||||
return listResult;
|
||||
}
|
||||
|
||||
private ExecuteResult executeSQL(String originalSql,DbType dbType,DlExecuteParam param) {
|
||||
String sql = originalSql;
|
||||
int pageNo = 0;
|
||||
private ExecuteResult executeSQL(String originalSql, DbType dbType, DlExecuteParam param) {
|
||||
int pageNo = 1;
|
||||
int pageSize = 0;
|
||||
Integer offset = null;
|
||||
Integer count = null;
|
||||
String sqlType = SqlTypeEnum.UNKNOWN.getCode();
|
||||
|
||||
// 解析sql分页
|
||||
SQLStatement sqlStatement;
|
||||
boolean autoLimit = false;
|
||||
try {
|
||||
sqlStatement = SQLUtils.parseSingleStatement(sql, dbType);
|
||||
sqlStatement = SQLUtils.parseSingleStatement(originalSql, dbType);
|
||||
// 是否需要代码帮忙分页
|
||||
if (sqlStatement instanceof SQLSelectStatement) {
|
||||
// 不是查询全部数据 而且 用户自己没有传分页
|
||||
autoLimit = BooleanUtils.isNotTrue(param.getPageSizeAll()) && SQLUtils.getLimit(sqlStatement,
|
||||
dbType)
|
||||
== null;
|
||||
if (autoLimit) {
|
||||
pageNo = Optional.ofNullable(param.getPageNo()).orElse(1);
|
||||
pageSize = Optional.ofNullable(param.getPageSize()).orElse(EasyToolsConstant.MAX_PAGE_SIZE);
|
||||
int offset = (pageNo - 1) * pageSize;
|
||||
try {
|
||||
sql = PagerUtils.limit(sql, dbType, offset, pageSize);
|
||||
} catch (Exception e) {
|
||||
autoLimit = false;
|
||||
}
|
||||
}
|
||||
pageNo = Optional.ofNullable(param.getPageNo()).orElse(1);
|
||||
pageSize = Optional.ofNullable(param.getPageSize()).orElse(EasyToolsConstant.MAX_PAGE_SIZE);
|
||||
offset = (pageNo - 1) * pageSize;
|
||||
count = pageSize;
|
||||
sqlType = SqlTypeEnum.SELECT.getCode();
|
||||
}
|
||||
} catch (ParserException e) {
|
||||
log.warn("解析sql失败:{}", sql, e);
|
||||
log.warn("解析sql失败:{}", originalSql, e);
|
||||
}
|
||||
|
||||
ExecuteResult executeResult = execute(sql);
|
||||
ExecuteResult executeResult = execute(originalSql, offset, count);
|
||||
executeResult.setSqlType(sqlType);
|
||||
executeResult.setOriginalSql(originalSql);
|
||||
// 自动分页
|
||||
if (autoLimit) {
|
||||
|
||||
if (SqlTypeEnum.SELECT.getCode().equals(sqlType)) {
|
||||
executeResult.setPageNo(pageNo);
|
||||
executeResult.setPageSize(pageSize);
|
||||
executeResult.setHasNextPage(
|
||||
CollectionUtils.size(executeResult.getDataList()) >= executeResult.getPageSize());
|
||||
} else {
|
||||
executeResult.setPageNo(1);
|
||||
executeResult.setPageNo(pageNo);
|
||||
executeResult.setPageSize(CollectionUtils.size(executeResult.getDataList()));
|
||||
executeResult.setHasNextPage(Boolean.FALSE);
|
||||
}
|
||||
|
||||
// Splice row numbers
|
||||
List<Header> newHeaderList = new ArrayList<>();
|
||||
newHeaderList.add(Header.builder()
|
||||
@@ -150,9 +140,6 @@ public class DlTemplateServiceImpl implements DlTemplateService {
|
||||
return executeResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private String calculateFuzzyTotal(int pageNo, int pageSize, ExecuteResult executeResult) {
|
||||
int dataSize = CollectionUtils.size(executeResult.getDataList());
|
||||
if (pageSize <= 0) {
|
||||
@@ -179,7 +166,7 @@ public class DlTemplateServiceImpl implements DlTemplateService {
|
||||
throw new BusinessException("dataSource.sqlAnalysisError");
|
||||
}
|
||||
sql = PagerUtils.count(sql, dbType);
|
||||
ExecuteResult executeResult = execute(sql);
|
||||
ExecuteResult executeResult = execute(sql, null, null);
|
||||
|
||||
List<List<String>> dataList = executeResult.getDataList();
|
||||
if (CollectionUtils.isEmpty(dataList)) {
|
||||
@@ -194,10 +181,10 @@ public class DlTemplateServiceImpl implements DlTemplateService {
|
||||
return DataResult.of(Long.valueOf(count));
|
||||
}
|
||||
|
||||
private ExecuteResult execute(String sql) {
|
||||
private ExecuteResult execute(String sql, Integer offset, Integer count) {
|
||||
ExecuteResult executeResult;
|
||||
try {
|
||||
executeResult = SQLExecutor.getInstance().execute(Chat2DBContext.getConnection(), sql);
|
||||
executeResult = SQLExecutor.getInstance().execute(sql, Chat2DBContext.getConnection(), true, offset, count);
|
||||
} catch (SQLException e) {
|
||||
log.warn("执行sql:{}异常", sql, e);
|
||||
executeResult = ExecuteResult.builder()
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package ai.chat2db.server.tools.common.util;
|
||||
|
||||
public class EasyIntegerUtils {
|
||||
|
||||
/**
|
||||
* 判断2个布尔值是否相同
|
||||
*
|
||||
* @param b1
|
||||
* @param b2
|
||||
* @param defaultValue 默认值 ,假设b1 b2为空的情况下 取哪个默认值
|
||||
* @return
|
||||
*/
|
||||
public static boolean equals(Integer b1, Integer b2, Integer defaultValue) {
|
||||
if (b1 == b2) {
|
||||
return true;
|
||||
}
|
||||
if (b1 == null) {
|
||||
b1 = defaultValue;
|
||||
}
|
||||
if (b2 == null) {
|
||||
b2 = defaultValue;
|
||||
}
|
||||
return b1 == b2;
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,7 @@ public class ChatController {
|
||||
/**
|
||||
* chat的超时时间
|
||||
*/
|
||||
private static final Long CHAT_TIMEOUT = Duration.ofMinutes(10).toMillis();
|
||||
private static final Long CHAT_TIMEOUT = Duration.ofMinutes(50).toMillis();
|
||||
|
||||
/**
|
||||
* 提示语最大token数
|
||||
|
||||
@@ -54,7 +54,7 @@ public class RestAIEventSourceListener extends EventSourceListener {
|
||||
}
|
||||
Message message = new Message();
|
||||
if (StringUtils.isNotBlank(data)) {
|
||||
message.setContent(data);
|
||||
message.setContent(data.replace("\"", ""));
|
||||
sseEmitter.send(SseEmitter.event()
|
||||
.id(id)
|
||||
.data(message)
|
||||
|
||||
@@ -35,7 +35,7 @@ public class ColumnRequest {
|
||||
/**
|
||||
* 是否为空
|
||||
*/
|
||||
private Boolean nullable;
|
||||
private Integer nullable;
|
||||
|
||||
/**
|
||||
* 是否主键
|
||||
|
||||
@@ -38,7 +38,7 @@ public class ColumnVO {
|
||||
/**
|
||||
* 是否为空
|
||||
*/
|
||||
private Boolean nullable;
|
||||
private Integer nullable;
|
||||
|
||||
/**
|
||||
* 是否主键
|
||||
|
||||
@@ -3,7 +3,6 @@ package ai.chat2db.spi.jdbc;
|
||||
import java.sql.Connection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ai.chat2db.spi.MetaData;
|
||||
import ai.chat2db.spi.model.Database;
|
||||
@@ -15,7 +14,6 @@ import ai.chat2db.spi.model.TableColumn;
|
||||
import ai.chat2db.spi.model.TableIndex;
|
||||
import ai.chat2db.spi.model.Trigger;
|
||||
import ai.chat2db.spi.sql.SQLExecutor;
|
||||
import org.apache.commons.beanutils.BeanUtils;
|
||||
|
||||
/**
|
||||
* @author jipengfei
|
||||
@@ -24,25 +22,12 @@ import org.apache.commons.beanutils.BeanUtils;
|
||||
public class DefaultMetaService implements MetaData {
|
||||
@Override
|
||||
public List<Database> databases(Connection connection) {
|
||||
List<String> dataBases = SQLExecutor.getInstance().databases(connection);
|
||||
return dataBases.stream().map(str -> Database.builder().name(str).build()).collect(Collectors.toList());
|
||||
|
||||
return SQLExecutor.getInstance().databases(connection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Schema> schemas(Connection connection,String databaseName) {
|
||||
List<Map<String, String>> maps = SQLExecutor.getInstance().schemas(connection, databaseName, null);
|
||||
return maps.stream().map(map -> map2Schema(map)).collect(Collectors.toList());
|
||||
|
||||
}
|
||||
|
||||
private Schema map2Schema(Map<String, String> map) {
|
||||
Schema schema = new Schema();
|
||||
try {
|
||||
BeanUtils.populate(schema, map);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return schema;
|
||||
return SQLExecutor.getInstance().schemas(connection, databaseName, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import ai.chat2db.server.tools.base.constant.EasyToolsConstant;
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -23,6 +24,7 @@ public class Database implements Serializable {
|
||||
/**
|
||||
* 数据库名字
|
||||
*/
|
||||
@JsonAlias({"TABLE_CAT"})
|
||||
private String name;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
package ai.chat2db.spi.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -26,16 +27,22 @@ public class Function {
|
||||
//SPECIFIC_NAME String => the name which uniquely identifies this function within its schema. This is a user specified, or DBMS generated, name that may be different then the FUNCTION_NAME for example with overload functions
|
||||
//
|
||||
|
||||
@JsonAlias({"FUNCTION_CAT"})
|
||||
private String databaseName;
|
||||
|
||||
@JsonAlias({"FUNCTION_SCHEM"})
|
||||
private String schemaName;
|
||||
|
||||
@JsonAlias({"FUNCTION_NAME"})
|
||||
private String functionName;
|
||||
|
||||
@JsonAlias({"REMARKS"})
|
||||
private String remarks;
|
||||
|
||||
@JsonAlias({"FUNCTION_TYPE"})
|
||||
private Short functionType;
|
||||
|
||||
@JsonAlias({"SPECIFIC_NAME"})
|
||||
private String specificName;
|
||||
|
||||
private String functionBody;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
package ai.chat2db.spi.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -26,16 +27,24 @@ public class Procedure {
|
||||
//SPECIFIC_NAME String => the name which uniquely identifies this procedure within its schema. This is a user specified, or DBMS generated, name that may be different then the PROCEDURE_NAME for example with overload procedures
|
||||
//
|
||||
|
||||
@JsonAlias({"PROCEDURE_CAT"})
|
||||
private String databaseName;
|
||||
|
||||
@JsonAlias({"PROCEDURE_SCHEM"})
|
||||
|
||||
private String schemaName;
|
||||
|
||||
@JsonAlias({"PROCEDURE_NAME"})
|
||||
private String procedureName;
|
||||
|
||||
@JsonAlias({"REMARKS"})
|
||||
private String remarks;
|
||||
|
||||
@JsonAlias({"PROCEDURE_TYPE"})
|
||||
|
||||
private Short procedureType;
|
||||
|
||||
@JsonAlias({"SPECIFIC_NAME"})
|
||||
private String specificName;
|
||||
|
||||
private String procedureBody;
|
||||
|
||||
@@ -4,6 +4,7 @@ package ai.chat2db.spi.model;
|
||||
import java.io.Serializable;
|
||||
|
||||
import ai.chat2db.server.tools.base.constant.EasyToolsConstant;
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -23,9 +24,11 @@ public class Schema implements Serializable {
|
||||
/**
|
||||
* databaseName
|
||||
*/
|
||||
@JsonAlias({"TABLE_CATALOG"})
|
||||
private String databaseName;
|
||||
/**
|
||||
* 数据名字
|
||||
*/
|
||||
@JsonAlias({"TABLE_SCHEM"})
|
||||
private String name;
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package ai.chat2db.spi.model;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -22,16 +23,21 @@ public class Table {
|
||||
/**
|
||||
* 表名
|
||||
*/
|
||||
@JsonAlias({"TABLE_NAME"})
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
@JsonAlias({"REMARKS"})
|
||||
|
||||
private String comment;
|
||||
|
||||
/**
|
||||
* DB 名
|
||||
*/
|
||||
@JsonAlias({"TABLE_SCHEM"})
|
||||
|
||||
private String schemaName;
|
||||
|
||||
/**
|
||||
@@ -52,11 +58,13 @@ public class Table {
|
||||
/**
|
||||
* 数据库名
|
||||
*/
|
||||
@JsonAlias("TABLE_CAT")
|
||||
private String databaseName;
|
||||
|
||||
/**
|
||||
* 表类型
|
||||
*/
|
||||
@JsonAlias("TABLE_TYPE")
|
||||
private String type;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ai.chat2db.spi.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -24,6 +25,7 @@ public class TableColumn {
|
||||
/**
|
||||
* 列名
|
||||
*/
|
||||
@JsonAlias({"COLUMN_NAME"})
|
||||
private String name;
|
||||
|
||||
/**
|
||||
@@ -35,18 +37,24 @@ public class TableColumn {
|
||||
* 列的类型
|
||||
* 比如 varchar(100) ,double(10,6)
|
||||
*/
|
||||
|
||||
@JsonAlias({"TYPE_NAME"})
|
||||
private String columnType;
|
||||
|
||||
/**
|
||||
* 列的数据类型
|
||||
* 比如 varchar ,double
|
||||
*/
|
||||
|
||||
@JsonAlias({"DATA_TYPE"})
|
||||
private Integer dataType;
|
||||
|
||||
|
||||
/**
|
||||
* 默认值
|
||||
*/
|
||||
|
||||
@JsonAlias({"COLUMN_DEF"})
|
||||
private String defaultValue;
|
||||
|
||||
/**
|
||||
@@ -58,6 +66,7 @@ public class TableColumn {
|
||||
/**
|
||||
* 注释
|
||||
*/
|
||||
@JsonAlias({"REMARKS"})
|
||||
private String comment;
|
||||
|
||||
/**
|
||||
@@ -68,11 +77,13 @@ public class TableColumn {
|
||||
/**
|
||||
* 空间名
|
||||
*/
|
||||
@JsonAlias({"TABLE_SCHEM"})
|
||||
private String schemaName;
|
||||
|
||||
/**
|
||||
* 数据库名
|
||||
*/
|
||||
@JsonAlias({"TABLE_CAT"})
|
||||
private String databaseName;
|
||||
|
||||
/**
|
||||
@@ -83,6 +94,8 @@ public class TableColumn {
|
||||
/**
|
||||
* column size.
|
||||
*/
|
||||
|
||||
@JsonAlias({"COLUMN_SIZE"})
|
||||
private Integer columnSize;
|
||||
|
||||
/**
|
||||
@@ -93,11 +106,15 @@ public class TableColumn {
|
||||
/**
|
||||
* the number of fractional digits. Null is returned for data types where DECIMAL_DIGITS is not applicable.
|
||||
*/
|
||||
|
||||
@JsonAlias({"DECIMAL_DIGITS"})
|
||||
private Integer decimalDigits;
|
||||
|
||||
/**
|
||||
* Radix (typically either 10 or 2)
|
||||
*/
|
||||
|
||||
@JsonAlias({"NUM_PREC_RADIX"})
|
||||
private Integer numPrecRadix;
|
||||
|
||||
/**
|
||||
@@ -127,12 +144,16 @@ public class TableColumn {
|
||||
/**
|
||||
* index of column in table (starting at 1)
|
||||
*/
|
||||
|
||||
@JsonAlias({"ORDINAL_POSITION"})
|
||||
private Integer ordinalPosition;
|
||||
|
||||
/**
|
||||
* ISO rules are used to determine the nullability for a column.
|
||||
*/
|
||||
private Boolean nullable;
|
||||
|
||||
@JsonAlias({"NULLABLE"})
|
||||
private Integer nullable;
|
||||
|
||||
/**
|
||||
* String => Indicates whether this is a generated column
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ai.chat2db.spi.model;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -20,11 +21,13 @@ public class TableIndexColumn {
|
||||
/**
|
||||
* 索引名称
|
||||
*/
|
||||
@JsonAlias({"INDEX_NAME"})
|
||||
private String indexName;
|
||||
|
||||
/**
|
||||
* 表名
|
||||
*/
|
||||
@JsonAlias ({"TABLE_NAME"})
|
||||
private String tableName;
|
||||
|
||||
/**
|
||||
@@ -42,17 +45,18 @@ public class TableIndexColumn {
|
||||
/**
|
||||
* 列名
|
||||
*/
|
||||
@JsonAlias({"COLUMN_NAME"})
|
||||
private String columnName;
|
||||
|
||||
/**
|
||||
* 顺序
|
||||
*/
|
||||
@JsonAlias({"ORDINAL_POSITION"})
|
||||
private Short ordinalPosition;
|
||||
|
||||
/**
|
||||
* 排序
|
||||
*
|
||||
* @see CollationEnum
|
||||
*/
|
||||
private String collation;
|
||||
|
||||
@@ -60,41 +64,49 @@ public class TableIndexColumn {
|
||||
/**
|
||||
* 索引所属schema
|
||||
*/
|
||||
@JsonAlias({"TABLE_SCHEM"})
|
||||
private String schemaName;
|
||||
|
||||
/**
|
||||
* 数据库名
|
||||
*/
|
||||
@JsonAlias({"TABLE_CAT"})
|
||||
private String databaseName;
|
||||
|
||||
/**
|
||||
* 是否唯一
|
||||
*/
|
||||
@JsonAlias({"NON_UNIQUE"})
|
||||
private Boolean nonUnique;
|
||||
|
||||
/**
|
||||
* index catalog (may be null); null when TYPE is tableIndexStatistic
|
||||
*/
|
||||
@JsonAlias({"INDEX_QUALIFIER"})
|
||||
private String indexQualifier;
|
||||
|
||||
/**
|
||||
* ASC_OR_DESC String => column sort sequence, "A" => ascending, "D" => descending, may be null if sort sequence is not supported; null when TYPE is tableIndexStatistic
|
||||
*/
|
||||
@JsonAlias({"ASC_OR_DESC"})
|
||||
private String ascOrDesc;
|
||||
|
||||
/**
|
||||
* CARDINALITY long => When TYPE is tableIndexStatistic, then this is the number of rows in the table; otherwise, it is the number of unique values in the index.
|
||||
*/
|
||||
@JsonAlias({"CARDINALITY"})
|
||||
private Long cardinality;
|
||||
|
||||
/**
|
||||
* When TYPE is tableIndexStatistic then this is the number of pages used for the table, otherwise it is the number of pages used for the current index.
|
||||
*/
|
||||
@JsonAlias({"PAGES"})
|
||||
private Long pages;
|
||||
|
||||
/**
|
||||
* Filter condition, if any. (may be null)
|
||||
*/
|
||||
@JsonAlias({"FILTER_CONDITION"})
|
||||
private String filterCondition;
|
||||
}
|
||||
|
||||
|
||||
@@ -78,13 +78,16 @@ public class IDriverManager {
|
||||
if (url == null) {
|
||||
throw new SQLException("The url cannot be null", "08001");
|
||||
}
|
||||
SQLException reason = null;
|
||||
DriverEntry driverEntry = DRIVER_ENTRY_MAP.get(driver.getJdbcDriver());
|
||||
if (driverEntry == null) {
|
||||
driverEntry = getJDBCDriver(driver);
|
||||
}
|
||||
try {
|
||||
return driverEntry.getDriver().connect(url, info);
|
||||
Connection connection = driverEntry.getDriver().connect(url, info);
|
||||
if(connection == null){
|
||||
throw new SQLException("driver.connect return null , No suitable driver found for url " +url ,"08001");
|
||||
}
|
||||
return connection;
|
||||
} catch (SQLException var7) {
|
||||
Connection con = tryConnectionAgain(driverEntry, url, info);
|
||||
if (con != null) {
|
||||
|
||||
@@ -5,22 +5,14 @@ import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ai.chat2db.server.tools.base.constant.EasyToolsConstant;
|
||||
import ai.chat2db.server.tools.common.util.I18nUtils;
|
||||
import ai.chat2db.spi.model.ExecuteResult;
|
||||
import ai.chat2db.spi.model.Header;
|
||||
import ai.chat2db.spi.model.Procedure;
|
||||
import ai.chat2db.spi.model.Table;
|
||||
import ai.chat2db.spi.model.TableColumn;
|
||||
import ai.chat2db.spi.model.TableIndex;
|
||||
import ai.chat2db.spi.model.TableIndexColumn;
|
||||
import ai.chat2db.spi.model.*;
|
||||
import ai.chat2db.spi.util.ResultSetUtils;
|
||||
import cn.hutool.core.date.TimeInterval;
|
||||
import com.google.common.collect.Lists;
|
||||
@@ -29,18 +21,10 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.jdbc.support.JdbcUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import static ai.chat2db.spi.util.ResultSetUtils.buildColumn;
|
||||
import static ai.chat2db.spi.util.ResultSetUtils.buildFunction;
|
||||
import static ai.chat2db.spi.util.ResultSetUtils.buildProcedure;
|
||||
import static ai.chat2db.spi.util.ResultSetUtils.buildTable;
|
||||
import static ai.chat2db.spi.util.ResultSetUtils.buildTableIndexColumn;
|
||||
|
||||
/**
|
||||
* Dbhub 统一数据库连接管理
|
||||
* TODO 长时间不用连接可以关闭,待优化
|
||||
*
|
||||
* @author jipengfei
|
||||
* @version : DbhubDataSource.java
|
||||
*/
|
||||
@Slf4j
|
||||
public class SQLExecutor {
|
||||
@@ -56,10 +40,6 @@ public class SQLExecutor {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
//public Connection connection throws SQLException {
|
||||
// return Chat2DBContext.connection;
|
||||
//}
|
||||
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@@ -165,17 +145,23 @@ public class SQLExecutor {
|
||||
* @throws SQLException
|
||||
*/
|
||||
public ExecuteResult execute(final String sql, Connection connection) throws SQLException {
|
||||
return execute(sql, connection, true);
|
||||
return execute(sql, connection, true, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行sql
|
||||
*
|
||||
* @param sql
|
||||
* @param connection
|
||||
* @param limitRowSize
|
||||
* @param offset
|
||||
* @param count
|
||||
* @return
|
||||
* @throws SQLException
|
||||
*/
|
||||
public ExecuteResult execute(final String sql, Connection connection, boolean limitSize) throws SQLException {
|
||||
public ExecuteResult execute(final String sql, Connection connection, boolean limitRowSize, Integer offset,
|
||||
Integer count)
|
||||
throws SQLException {
|
||||
Assert.notNull(sql, "SQL must not be null");
|
||||
log.info("execute:{}", sql);
|
||||
|
||||
@@ -209,13 +195,22 @@ public class SQLExecutor {
|
||||
List<List<String>> dataList = Lists.newArrayList();
|
||||
executeResult.setDataList(dataList);
|
||||
|
||||
int n = 0;
|
||||
while (rs.next() && n < EasyToolsConstant.MAX_PAGE_SIZE) {
|
||||
n++;
|
||||
if (offset == null || offset < 0) {
|
||||
offset = 0;
|
||||
}
|
||||
int rowNumber = 0;
|
||||
int rowCount = 1;
|
||||
while (rs.next()) {
|
||||
if (rowNumber++ < offset) {
|
||||
continue;
|
||||
}
|
||||
List<String> row = Lists.newArrayListWithExpectedSize(col);
|
||||
dataList.add(row);
|
||||
for (int i = 1; i <= col; i++) {
|
||||
row.add(ai.chat2db.spi.util.JdbcUtils.getResultSetValue(rs, i, limitSize));
|
||||
row.add(ai.chat2db.spi.util.JdbcUtils.getResultSetValue(rs, i, limitRowSize));
|
||||
}
|
||||
if (count != null && count > 0 && rowCount++ >= count) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
executeResult.setDuration(timeInterval.interval());
|
||||
@@ -240,7 +235,7 @@ public class SQLExecutor {
|
||||
* @throws SQLException
|
||||
*/
|
||||
public ExecuteResult execute(Connection connection, String sql) throws SQLException {
|
||||
return execute(sql, connection, true);
|
||||
return execute(sql, connection, true, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,58 +244,46 @@ public class SQLExecutor {
|
||||
* @param connection
|
||||
* @return
|
||||
*/
|
||||
public List<String> databases(Connection connection) {
|
||||
List<String> tables = Lists.newArrayList();
|
||||
public List<Database> databases(Connection connection) {
|
||||
try (ResultSet resultSet = connection.getMetaData().getCatalogs();) {
|
||||
if (resultSet != null) {
|
||||
while (resultSet.next()) {
|
||||
tables.add(resultSet.getString("TABLE_CAT"));
|
||||
}
|
||||
}
|
||||
return ResultSetUtils.toObjectList(resultSet, Database.class);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return tables;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的schema
|
||||
*
|
||||
* @param connection
|
||||
* @param databaseName
|
||||
* @param schemaName
|
||||
* @return
|
||||
* Retrieves the schema names available in this database. The results are ordered by TABLE_CATALOG and TABLE_SCHEM.
|
||||
* The schema columns are:
|
||||
* TABLE_SCHEM String => schema name
|
||||
* TABLE_CATALOG String => catalog name (may be null)
|
||||
* Params:
|
||||
* catalog – a catalog name; must match the catalog name as it is stored in the database;"" retrieves those without
|
||||
* a catalog; null means catalog name should not be used to narrow down the search. schemaPattern – a schema name;
|
||||
* must match the schema name as it is stored in the database; null means schema name should not be used to narrow
|
||||
* down the search.
|
||||
* Returns:
|
||||
* a ResultSet object in which each row is a schema description
|
||||
* Throws:
|
||||
* SQLException – if a database access error occurs
|
||||
* Since:
|
||||
* 1.6
|
||||
* See Also:
|
||||
* getSearchStringEscape
|
||||
*/
|
||||
public List<Map<String, String>> schemas(Connection connection, String databaseName, String schemaName) {
|
||||
List<Map<String, String>> schemaList = Lists.newArrayList();
|
||||
public List<Schema> schemas(Connection connection, String databaseName, String schemaName) {
|
||||
if (StringUtils.isEmpty(databaseName) && StringUtils.isEmpty(schemaName)) {
|
||||
try (ResultSet resultSet = connection.getMetaData().getSchemas()) {
|
||||
if (resultSet != null) {
|
||||
while (resultSet.next()) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("name", resultSet.getString("TABLE_SCHEM"));
|
||||
map.put("databaseName", resultSet.getString("TABLE_CATALOG"));
|
||||
schemaList.add(map);
|
||||
}
|
||||
}
|
||||
return ResultSetUtils.toObjectList(resultSet, Schema.class);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("Get schemas error", e);
|
||||
}
|
||||
return schemaList;
|
||||
}
|
||||
try (ResultSet resultSet = connection.getMetaData().getSchemas(databaseName, schemaName)) {
|
||||
if (resultSet != null) {
|
||||
while (resultSet.next()) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("name", resultSet.getString("TABLE_SCHEM"));
|
||||
map.put("databaseName", resultSet.getString("TABLE_CATALOG"));
|
||||
schemaList.add(map);
|
||||
}
|
||||
}
|
||||
return ResultSetUtils.toObjectList(resultSet, Schema.class);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("Get schemas error", e);
|
||||
}
|
||||
return schemaList;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -315,23 +298,12 @@ public class SQLExecutor {
|
||||
*/
|
||||
public List<Table> tables(Connection connection, String databaseName, String schemaName, String tableName,
|
||||
String types[]) {
|
||||
List<Table> tables = Lists.newArrayList();
|
||||
int n = 0;
|
||||
try (ResultSet resultSet = connection.getMetaData().getTables(databaseName, schemaName, tableName,
|
||||
types)) {
|
||||
if (resultSet != null) {
|
||||
while (resultSet.next()) {
|
||||
n++;
|
||||
tables.add(buildTable(resultSet));
|
||||
if (n >= 1000) {// 最多只取1000条
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ResultSetUtils.toObjectList(resultSet, Table.class);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return tables;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -346,40 +318,29 @@ public class SQLExecutor {
|
||||
*/
|
||||
public List<TableColumn> columns(Connection connection, String databaseName, String schemaName, String tableName,
|
||||
String columnName) {
|
||||
List<TableColumn> tableColumns = Lists.newArrayList();
|
||||
try (ResultSet resultSet = connection.getMetaData().getColumns(databaseName, schemaName, tableName,
|
||||
columnName)) {
|
||||
if (resultSet != null) {
|
||||
while (resultSet.next()) {
|
||||
tableColumns.add(buildColumn(resultSet));
|
||||
}
|
||||
}
|
||||
return ResultSetUtils.toObjectList(resultSet, TableColumn.class);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return tableColumns;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的数据库表索引
|
||||
* get all table index info
|
||||
*
|
||||
* @param connection
|
||||
* @param databaseName
|
||||
* @param schemaName
|
||||
* @param tableName
|
||||
* @return
|
||||
* @param connection connection
|
||||
* @param databaseName databaseName of the index
|
||||
* @param schemaName schemaName of the index
|
||||
* @param tableName tableName of the index
|
||||
* @return List<TableIndex> table index list
|
||||
*/
|
||||
public List<TableIndex> indexes(Connection connection, String databaseName, String schemaName, String tableName) {
|
||||
List<TableIndex> tableIndices = Lists.newArrayList();
|
||||
try (ResultSet resultSet = connection.getMetaData().getIndexInfo(databaseName, schemaName, tableName,
|
||||
false,
|
||||
false)) {
|
||||
List<TableIndexColumn> tableIndexColumns = Lists.newArrayList();
|
||||
|
||||
while (resultSet != null && resultSet.next()) {
|
||||
tableIndexColumns.add(buildTableIndexColumn(resultSet));
|
||||
}
|
||||
|
||||
List<TableIndexColumn> tableIndexColumns = ResultSetUtils.toObjectList(resultSet, TableIndexColumn.class);
|
||||
tableIndexColumns.stream().filter(c -> c.getIndexName() != null).collect(
|
||||
Collectors.groupingBy(TableIndexColumn::getIndexName)).entrySet()
|
||||
.stream().forEach(entry -> {
|
||||
@@ -400,44 +361,36 @@ public class SQLExecutor {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的函数
|
||||
* Get all functions available in a catalog.
|
||||
*
|
||||
* @param connection
|
||||
* @param databaseName
|
||||
* @param schemaName
|
||||
* @return
|
||||
* @param connection connection
|
||||
* @param databaseName databaseName of the function
|
||||
* @param schemaName schemaName of the function
|
||||
* @return List<Function>
|
||||
*/
|
||||
public List<ai.chat2db.spi.model.Function> functions(Connection connection, String databaseName,
|
||||
String schemaName) {
|
||||
List<ai.chat2db.spi.model.Function> functions = Lists.newArrayList();
|
||||
try (ResultSet resultSet = connection.getMetaData().getFunctions(databaseName, schemaName, null);) {
|
||||
while (resultSet != null && resultSet.next()) {
|
||||
functions.add(buildFunction(resultSet));
|
||||
}
|
||||
return ResultSetUtils.toObjectList(resultSet, ai.chat2db.spi.model.Function.class);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return functions;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的存储过程
|
||||
* procedure list
|
||||
*
|
||||
* @param connection
|
||||
* @param databaseName
|
||||
* @param schemaName
|
||||
* @return
|
||||
* @param connection connection
|
||||
* @param databaseName databaseName
|
||||
* @param schemaName schemaName
|
||||
* @return List<Procedure>
|
||||
*/
|
||||
public List<Procedure> procedures(Connection connection, String databaseName, String schemaName) {
|
||||
List<Procedure> procedures = Lists.newArrayList();
|
||||
try (ResultSet resultSet = connection.getMetaData().getProcedures(databaseName, schemaName, null)) {
|
||||
while (resultSet != null && resultSet.next()) {
|
||||
procedures.add(buildProcedure(resultSet));
|
||||
}
|
||||
return ResultSetUtils.toObjectList(resultSet, Procedure.class);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return procedures;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ai.chat2db.spi.util;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Blob;
|
||||
import java.sql.Clob;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
@@ -144,24 +145,51 @@ public class JdbcUtils {
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
if (obj instanceof BigDecimal bigDecimal) {
|
||||
return bigDecimal.toPlainString();
|
||||
} else if (obj instanceof Double d) {
|
||||
return BigDecimal.valueOf(d).toPlainString();
|
||||
} else if (obj instanceof Float f) {
|
||||
return BigDecimal.valueOf(f).toPlainString();
|
||||
} else if (obj instanceof Clob) {
|
||||
return largeString(rs, index, limitSize);
|
||||
} else if (obj instanceof byte[]) {
|
||||
return largeString(rs, index, limitSize);
|
||||
try {
|
||||
if (obj instanceof BigDecimal bigDecimal) {
|
||||
return bigDecimal.toPlainString();
|
||||
} else if (obj instanceof Double d) {
|
||||
return BigDecimal.valueOf(d).toPlainString();
|
||||
} else if (obj instanceof Float f) {
|
||||
return BigDecimal.valueOf(f).toPlainString();
|
||||
} else if (obj instanceof Clob) {
|
||||
return largeString(rs, index, limitSize);
|
||||
} else if (obj instanceof byte[]) {
|
||||
return largeString(rs, index, limitSize);
|
||||
} else if (obj instanceof Blob blob) {
|
||||
return largeStringBlob(blob, limitSize);
|
||||
}
|
||||
return rs.getString(index);
|
||||
} catch (Exception e) {
|
||||
log.warn("解析数失败:{},{}", index, obj, e);
|
||||
return obj.toString();
|
||||
}
|
||||
return rs.getString(index);
|
||||
}
|
||||
|
||||
private static String largeStringBlob(Blob blob, boolean limitSize) throws SQLException {
|
||||
if (blob == null) {
|
||||
return null;
|
||||
}
|
||||
int length = Math.toIntExact(blob.length());
|
||||
if (limitSize && length > MAX_RESULT_SIZE) {
|
||||
length = Math.toIntExact(MAX_RESULT_SIZE);
|
||||
}
|
||||
byte[] data = blob.getBytes(1, length);
|
||||
String result = new String(data);
|
||||
|
||||
if (length > MAX_RESULT_SIZE) {
|
||||
return "[ " + DataSizeUtil.format(MAX_RESULT_SIZE) + " of " + DataSizeUtil.format(length)
|
||||
+ " ,"
|
||||
+ I18nUtils.getMessage("execute.exportCsv") + " ] " + result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String largeString(ResultSet rs, int index, boolean limitSize) throws SQLException {
|
||||
String result = rs.getString(index);
|
||||
if (result == null) {
|
||||
return null;
|
||||
|
||||
}
|
||||
if (!limitSize) {
|
||||
return result;
|
||||
|
||||
@@ -4,8 +4,16 @@ package ai.chat2db.spi.util;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import ai.chat2db.spi.model.*;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import static ai.chat2db.spi.util.JdbcUtils.getResultSetValue;
|
||||
|
||||
/**
|
||||
* @author jipengfei
|
||||
@@ -13,99 +21,79 @@ import ai.chat2db.spi.model.*;
|
||||
*/
|
||||
public class ResultSetUtils {
|
||||
|
||||
public static ai.chat2db.spi.model.Function buildFunction(ResultSet resultSet) {
|
||||
ai.chat2db.spi.model.Function function
|
||||
= new ai.chat2db.spi.model.Function();
|
||||
|
||||
public static <T> T toObject(ResultSet rs, Class<T> clazz) {
|
||||
try {
|
||||
function.setDatabaseName(getString(resultSet, "FUNCTION_CAT"));
|
||||
function.setSchemaName(getString(resultSet, "FUNCTION_SCHEM"));
|
||||
function.setFunctionName(getString(resultSet, "FUNCTION_NAME"));
|
||||
function.setRemarks(getString(resultSet, "REMARKS"));
|
||||
function.setFunctionType(resultSet.getShort("FUNCTION_TYPE"));
|
||||
function.setSpecificName(getString(resultSet, "SPECIFIC_NAME"));
|
||||
if (rs == null || clazz == null) {
|
||||
return null;
|
||||
}
|
||||
ResultSetMetaData resultSetMetaData = rs.getMetaData();
|
||||
int col = resultSetMetaData.getColumnCount();
|
||||
List<String> headerList = getRsHeader(rs);
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
for (int i = 1; i <= col; i++) {
|
||||
map.put(headerList.get(i), getResultSetValue(rs, i, true));
|
||||
}
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
|
||||
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
//mapper.configure(DeserializationFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
|
||||
|
||||
return mapper.convertValue(map, clazz);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return function;
|
||||
}
|
||||
|
||||
public static Procedure buildProcedure(ResultSet resultSet) {
|
||||
Procedure procedure = new Procedure();
|
||||
private static List<String> getRsHeader(ResultSet rs) {
|
||||
try {
|
||||
procedure.setDatabaseName(getString(resultSet, "PROCEDURE_CAT"));
|
||||
procedure.setSchemaName(getString(resultSet, "PROCEDURE_SCHEM"));
|
||||
procedure.setProcedureName(getString(resultSet, "PROCEDURE_NAME"));
|
||||
procedure.setRemarks(getString(resultSet, "REMARKS"));
|
||||
procedure.setProcedureType(resultSet.getShort("PROCEDURE_TYPE"));
|
||||
procedure.setSpecificName(getString(resultSet, "SPECIFIC_NAME"));
|
||||
ResultSetMetaData resultSetMetaData = rs.getMetaData();
|
||||
int col = resultSetMetaData.getColumnCount();
|
||||
List<String> headerList = Lists.newArrayListWithExpectedSize(col);
|
||||
for (int i = 1; i <= col; i++) {
|
||||
headerList.add(getColumnName(resultSetMetaData, i));
|
||||
}
|
||||
return headerList;
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return procedure;
|
||||
}
|
||||
|
||||
|
||||
public static TableIndexColumn buildTableIndexColumn(ResultSet resultSet) throws SQLException {
|
||||
TableIndexColumn tableIndexColumn = new TableIndexColumn();
|
||||
tableIndexColumn.setColumnName(getString(resultSet, "COLUMN_NAME"));
|
||||
tableIndexColumn.setIndexName(getString(resultSet, "INDEX_NAME"));
|
||||
tableIndexColumn.setAscOrDesc(getString(resultSet, "ASC_OR_DESC"));
|
||||
tableIndexColumn.setCardinality(resultSet.getLong("CARDINALITY"));
|
||||
tableIndexColumn.setPages(resultSet.getLong("PAGES"));
|
||||
tableIndexColumn.setFilterCondition(getString(resultSet, "FILTER_CONDITION"));
|
||||
tableIndexColumn.setIndexQualifier(getString(resultSet, "INDEX_QUALIFIER"));
|
||||
// tableIndexColumn.setIndexType(resultSet.getShort("TYPE"));
|
||||
tableIndexColumn.setNonUnique(resultSet.getBoolean("NON_UNIQUE"));
|
||||
tableIndexColumn.setOrdinalPosition(resultSet.getShort("ORDINAL_POSITION"));
|
||||
tableIndexColumn.setDatabaseName(getString(resultSet, "TABLE_CAT"));
|
||||
tableIndexColumn.setSchemaName(getString(resultSet, "TABLE_SCHEM"));
|
||||
tableIndexColumn.setTableName(getString(resultSet, "TABLE_NAME"));
|
||||
return tableIndexColumn;
|
||||
}
|
||||
|
||||
public static TableColumn buildColumn(ResultSet resultSet) throws SQLException {
|
||||
TableColumn tableColumn = new TableColumn();
|
||||
tableColumn.setDatabaseName(getString(resultSet, "TABLE_CAT"));
|
||||
tableColumn.setSchemaName(getString(resultSet, "TABLE_SCHEM"));
|
||||
tableColumn.setTableName(getString(resultSet, "TABLE_NAME"));
|
||||
tableColumn.setName(getString(resultSet, "COLUMN_NAME"));
|
||||
tableColumn.setComment(getString(resultSet, "REMARKS"));
|
||||
tableColumn.setDefaultValue(getString(resultSet, "COLUMN_DEF"));
|
||||
tableColumn.setColumnType(getString(resultSet, "TYPE_NAME"));
|
||||
tableColumn.setColumnSize(resultSet.getInt("COLUMN_SIZE"));
|
||||
tableColumn.setDataType(resultSet.getInt("DATA_TYPE"));
|
||||
tableColumn.setNullable(resultSet.getInt("NULLABLE") == 1);
|
||||
tableColumn.setOrdinalPosition(resultSet.getInt("ORDINAL_POSITION"));
|
||||
//tableColumn.setAutoIncrement("YES".equals(getString,resultSet,"IS_AUTOINCREMENT")));
|
||||
//tableColumn.setGeneratedColumn("YES".equals(getString,resultSet,"IS_GENERATEDCOLUMN")));
|
||||
tableColumn.setOrdinalPosition(resultSet.getInt("ORDINAL_POSITION"));
|
||||
tableColumn.setDecimalDigits(resultSet.getInt("DECIMAL_DIGITS"));
|
||||
tableColumn.setNumPrecRadix(resultSet.getInt("NUM_PREC_RADIX"));
|
||||
tableColumn.setCharOctetLength(resultSet.getInt("CHAR_OCTET_LENGTH"));
|
||||
return tableColumn;
|
||||
}
|
||||
|
||||
public static Table buildTable(ResultSet resultSet) throws SQLException {
|
||||
Table table = new Table();
|
||||
table.setName(getString(resultSet, "TABLE_NAME"));
|
||||
table.setComment(getString(resultSet, "REMARKS"));
|
||||
table.setDatabaseName(getString(resultSet, "TABLE_CAT"));
|
||||
table.setSchemaName(getString(resultSet, "TABLE_SCHEM"));
|
||||
table.setType(getString(resultSet, "TABLE_TYPE"));
|
||||
return table;
|
||||
}
|
||||
|
||||
private static String getString(ResultSet resultSet, String name) {
|
||||
if (resultSet == null) {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param rs
|
||||
* @param clazz
|
||||
* @return
|
||||
* @param <T>
|
||||
*/
|
||||
public static <T> List<T> toObjectList(ResultSet rs, Class<T> clazz) {
|
||||
try {
|
||||
return resultSet.getString(name);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
if (rs == null || clazz == null) {
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
List<T> list = Lists.newArrayList();
|
||||
ResultSetMetaData rsMetaData = rs.getMetaData();
|
||||
int col = rsMetaData.getColumnCount();
|
||||
List<String> headerList = getRsHeader(rs);
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
|
||||
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
while (rs.next()) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
for (int i = 1; i <= col; i++) {
|
||||
map.put(headerList.get(i-1), rs.getObject(i));
|
||||
}
|
||||
T obj = mapper.convertValue(map, clazz);
|
||||
|
||||
list.add(obj);
|
||||
}
|
||||
return list;
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String getColumnName(ResultSetMetaData resultSetMetaData, int column) throws SQLException {
|
||||
String columnLabel = resultSetMetaData.getColumnLabel(column);
|
||||
if (columnLabel != null) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ai.chat2db.server.tools.common.util.EasyIntegerUtils;
|
||||
import com.alibaba.druid.DbType;
|
||||
import com.alibaba.druid.sql.SQLUtils;
|
||||
import com.alibaba.druid.sql.ast.SQLDataTypeImpl;
|
||||
@@ -85,7 +86,7 @@ public class SqlUtils {
|
||||
mySqlCreateTableStatement.addColumn(sqlColumnDefinition);
|
||||
sqlColumnDefinition.setName(tableColumn.getName());
|
||||
sqlColumnDefinition.setDataType(new SQLDataTypeImpl(tableColumn.getColumnType()));
|
||||
if (BooleanUtils.isNotFalse(tableColumn.getNullable())) {
|
||||
if (tableColumn.getNullable()==1) {
|
||||
sqlColumnDefinition.addConstraint(new SQLNullConstraint());
|
||||
} else {
|
||||
sqlColumnDefinition.addConstraint(new SQLNotNullConstraint());
|
||||
@@ -221,7 +222,7 @@ public class SqlUtils {
|
||||
sqlAlterTableAddColumn.addColumn(sqlColumnDefinition);
|
||||
sqlColumnDefinition.setName(newTableColumn.getName());
|
||||
sqlColumnDefinition.setDataType(new SQLDataTypeImpl(newTableColumn.getColumnType()));
|
||||
if (BooleanUtils.isNotTrue(newTableColumn.getNullable())) {
|
||||
if (newTableColumn.getNullable()!=1) {
|
||||
sqlColumnDefinition.addConstraint(new SQLNotNullConstraint());
|
||||
}
|
||||
if (!Objects.isNull(newTableColumn.getDefaultValue())) {
|
||||
@@ -236,7 +237,7 @@ public class SqlUtils {
|
||||
// 代表可能修改字段 或者没变
|
||||
boolean hasChange = !StringUtils.equals(oldTableColumn.getName(), newTableColumn.getName())
|
||||
|| !StringUtils.equals(oldTableColumn.getColumnType(), newTableColumn.getColumnType())
|
||||
|| !EasyBooleanUtils.equals(oldTableColumn.getNullable(), newTableColumn.getNullable(), Boolean.TRUE)
|
||||
|| !EasyIntegerUtils.equals(oldTableColumn.getNullable(), newTableColumn.getNullable(), 1)
|
||||
|| !StringUtils.equals(oldTableColumn.getDefaultValue(), newTableColumn.getDefaultValue())
|
||||
|| !EasyBooleanUtils.equals(oldTableColumn.getAutoIncrement(), newTableColumn.getAutoIncrement(),
|
||||
Boolean.FALSE)
|
||||
@@ -256,7 +257,7 @@ public class SqlUtils {
|
||||
mySqlAlterTableChangeColumn.setNewColumnDefinition(sqlColumnDefinition);
|
||||
sqlColumnDefinition.setName(newTableColumn.getName());
|
||||
sqlColumnDefinition.setDataType(new SQLDataTypeImpl(newTableColumn.getColumnType()));
|
||||
if (BooleanUtils.isNotTrue(newTableColumn.getNullable())) {
|
||||
if (newTableColumn.getNullable()!=1) {
|
||||
sqlColumnDefinition.addConstraint(new SQLNotNullConstraint());
|
||||
}
|
||||
if (!Objects.isNull(newTableColumn.getDefaultValue())) {
|
||||
@@ -274,7 +275,7 @@ public class SqlUtils {
|
||||
mySqlAlterTableModifyColumn.setNewColumnDefinition(sqlColumnDefinition);
|
||||
sqlColumnDefinition.setName(newTableColumn.getName());
|
||||
sqlColumnDefinition.setDataType(new SQLDataTypeImpl(newTableColumn.getColumnType()));
|
||||
if (BooleanUtils.isNotTrue(newTableColumn.getNullable())) {
|
||||
if (newTableColumn.getNullable()!=1) {
|
||||
sqlColumnDefinition.addConstraint(new SQLNotNullConstraint());
|
||||
}
|
||||
if (!Objects.isNull(newTableColumn.getDefaultValue())) {
|
||||
|
||||
Reference in New Issue
Block a user