diff --git a/.github/workflows/release_test.yml b/.github/workflows/release_test.yml index d71beff8..14867d07 100644 --- a/.github/workflows/release_test.yml +++ b/.github/workflows/release_test.yml @@ -7,6 +7,8 @@ on: push: branches: - "release_test" + - "release_test_2" + - "release_test_3" # Workflow's jobs # 一共需要3台电脑运行 diff --git a/CHANGELOG.md b/CHANGELOG.md index 167fb756..75195c6f 100644 --- a/CHANGELOG.md +++ b/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 diff --git a/README.md b/README.md index ea6cc509..ba1562cc 100644 --- a/README.md +++ b/README.md @@ -192,6 +192,8 @@ Follow our WeChat public account. + Click and join discord server + ## ❤️ Acknowledgements Thanks to all the students who contributed to Chat2DB~ diff --git a/README_CN.md b/README_CN.md index 110885dc..749357b5 100644 --- a/README_CN.md +++ b/README_CN.md @@ -14,21 +14,15 @@
-

分享 Chat2DB

-

- -Share on Telegram - -Share on Reddit - -

- -**许可说明**: Chat2DB 开源内容仅供个人免费使用,如想将该项目用于商业用途,请先联系该项目作者。 - -Languages: 中文 [English](README.md) + Languages: 中文 [English](README.md) + +如果觉得 Chat2DB 对您有帮助的话,请帮忙github star +的右上角点个⭐ Star 和 Fork,您的支持是 Chat2DB 变得更好最大的动力
+ + ## 案例视频 https://github.com/chat2db/Chat2DB/assets/22975773/b58db908-5768-4a71-aa30-135d202e505f diff --git a/chat2db-server/chat2db-plugins/chat2db-dm/src/main/java/ai/chat2db/plugin/dm/DMMetaData.java b/chat2db-server/chat2db-plugins/chat2db-dm/src/main/java/ai/chat2db/plugin/dm/DMMetaData.java index 009d12e1..f6555f64 100644 --- a/chat2db-server/chat2db-plugins/chat2db-dm/src/main/java/ai/chat2db/plugin/dm/DMMetaData.java +++ b/chat2db-server/chat2db-plugins/chat2db-dm/src/main/java/ai/chat2db/plugin/dm/DMMetaData.java @@ -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, diff --git a/chat2db-server/chat2db-plugins/chat2db-sqlserver/src/main/java/ai/chat2db/plugin/sqlserver/SqlServerMetaData.java b/chat2db-server/chat2db-plugins/chat2db-sqlserver/src/main/java/ai/chat2db/plugin/sqlserver/SqlServerMetaData.java index 35af5786..d83b3135 100644 --- a/chat2db-server/chat2db-plugins/chat2db-sqlserver/src/main/java/ai/chat2db/plugin/sqlserver/SqlServerMetaData.java +++ b/chat2db-server/chat2db-plugins/chat2db-sqlserver/src/main/java/ai/chat2db/plugin/sqlserver/SqlServerMetaData.java @@ -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 functions(Connection connection, String databaseName, String schemaName) { - List functions = SQLExecutor.getInstance().functions(connection, databaseName, schemaName); - return functions.stream().map(function -> removeVersion(function)).collect(Collectors.toList()); + List functions = new ArrayList<>(); +// List 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 procedures(Connection connection, String databaseName, String schemaName) { - List procedures = SQLExecutor.getInstance().procedures(connection, databaseName, schemaName); - return procedures.stream().map(procedure -> removeVersion(procedure)).collect(Collectors.toList()); + List 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) { diff --git a/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/DatabaseServiceImpl.java b/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/DatabaseServiceImpl.java index bdf1cf8b..589bbc7f 100644 --- a/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/DatabaseServiceImpl.java +++ b/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/DatabaseServiceImpl.java @@ -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 getSchemaList(String databaseName, Connection connection) { + MetaData metaData = Chat2DBContext.getMetaData(); + List schemas = metaData.schemas(connection,databaseName); + sortSchema(schemas,connection); + return schemas; + } + + private void sortSchema(List 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 queryDatabaseSchema(MetaDataQueryParam param) { MetaSchema metaSchema = new MetaSchema(); diff --git a/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/DlTemplateServiceImpl.java b/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/DlTemplateServiceImpl.java index 0ed82aa4..c61644bc 100644 --- a/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/DlTemplateServiceImpl.java +++ b/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/DlTemplateServiceImpl.java @@ -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 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
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> 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() diff --git a/chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/util/EasyIntegerUtils.java b/chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/util/EasyIntegerUtils.java new file mode 100644 index 00000000..1e64099d --- /dev/null +++ b/chat2db-server/chat2db-server-tools/chat2db-server-tools-common/src/main/java/ai/chat2db/server/tools/common/util/EasyIntegerUtils.java @@ -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; + } +} diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java index 634ac703..89ceb821 100644 --- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java +++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java @@ -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数 diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/listener/RestAIEventSourceListener.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/listener/RestAIEventSourceListener.java index 8f6aa47d..657c458f 100644 --- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/listener/RestAIEventSourceListener.java +++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/listener/RestAIEventSourceListener.java @@ -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) diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/ColumnRequest.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/ColumnRequest.java index d0c30cd4..308faf17 100644 --- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/ColumnRequest.java +++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/ColumnRequest.java @@ -35,7 +35,7 @@ public class ColumnRequest { /** * 是否为空 */ - private Boolean nullable; + private Integer nullable; /** * 是否主键 diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/vo/ColumnVO.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/vo/ColumnVO.java index a6f4b90b..c8757b6f 100644 --- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/vo/ColumnVO.java +++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/vo/ColumnVO.java @@ -38,7 +38,7 @@ public class ColumnVO { /** * 是否为空 */ - private Boolean nullable; + private Integer nullable; /** * 是否主键 diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/jdbc/DefaultMetaService.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/jdbc/DefaultMetaService.java index e37b9189..002af04c 100644 --- a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/jdbc/DefaultMetaService.java +++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/jdbc/DefaultMetaService.java @@ -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 databases(Connection connection) { - List 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 schemas(Connection connection,String databaseName) { - List> maps = SQLExecutor.getInstance().schemas(connection, databaseName, null); - return maps.stream().map(map -> map2Schema(map)).collect(Collectors.toList()); - - } - - private Schema map2Schema(Map map) { - Schema schema = new Schema(); - try { - BeanUtils.populate(schema, map); - } catch (Exception e) { - } - return schema; + return SQLExecutor.getInstance().schemas(connection, databaseName, null); } @Override diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Database.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Database.java index ffc434f9..02e23ef1 100644 --- a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Database.java +++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Database.java @@ -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; /** diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Function.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Function.java index 13876e75..b111ed8f 100644 --- a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Function.java +++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Function.java @@ -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; diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Procedure.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Procedure.java index 97c4986f..3c48489d 100644 --- a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Procedure.java +++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Procedure.java @@ -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; diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Schema.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Schema.java index c72e3dfb..a54b927a 100644 --- a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Schema.java +++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Schema.java @@ -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; } \ No newline at end of file diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Table.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Table.java index 188c5d82..bbd3dcb2 100644 --- a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Table.java +++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/Table.java @@ -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; /** diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/TableColumn.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/TableColumn.java index 530654ba..7d58d1d5 100644 --- a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/TableColumn.java +++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/TableColumn.java @@ -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 diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/TableIndexColumn.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/TableIndexColumn.java index 810f8e37..091e9cd2 100644 --- a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/TableIndexColumn.java +++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/TableIndexColumn.java @@ -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; } diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/IDriverManager.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/IDriverManager.java index 83ae4e2b..d841646a 100644 --- a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/IDriverManager.java +++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/IDriverManager.java @@ -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) { diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/SQLExecutor.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/SQLExecutor.java index a109cf40..06f1fc6d 100644 --- a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/SQLExecutor.java +++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/SQLExecutor.java @@ -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> 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 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 databases(Connection connection) { - List tables = Lists.newArrayList(); + public List 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> schemas(Connection connection, String databaseName, String schemaName) { - List> schemaList = Lists.newArrayList(); + public List 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 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 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 tables(Connection connection, String databaseName, String schemaName, String tableName, String types[]) { - List
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 columns(Connection connection, String databaseName, String schemaName, String tableName, String columnName) { - List 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 table index list */ public List indexes(Connection connection, String databaseName, String schemaName, String tableName) { List tableIndices = Lists.newArrayList(); try (ResultSet resultSet = connection.getMetaData().getIndexInfo(databaseName, schemaName, tableName, false, false)) { - List tableIndexColumns = Lists.newArrayList(); - - while (resultSet != null && resultSet.next()) { - tableIndexColumns.add(buildTableIndexColumn(resultSet)); - } - + List 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 */ public List functions(Connection connection, String databaseName, String schemaName) { - List 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 */ public List procedures(Connection connection, String databaseName, String schemaName) { - List 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; } } diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/JdbcUtils.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/JdbcUtils.java index e0b49966..368e038c 100644 --- a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/JdbcUtils.java +++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/JdbcUtils.java @@ -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; diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/ResultSetUtils.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/ResultSetUtils.java index f26a446d..76c1fce9 100644 --- a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/ResultSetUtils.java +++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/ResultSetUtils.java @@ -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 toObject(ResultSet rs, Class 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 headerList = getRsHeader(rs); + Map 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 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 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 + */ + public static List toObjectList(ResultSet rs, Class clazz) { try { - return resultSet.getString(name); - } catch (Exception e) { - return null; + if (rs == null || clazz == null) { + return Lists.newArrayList(); + } + List list = Lists.newArrayList(); + ResultSetMetaData rsMetaData = rs.getMetaData(); + int col = rsMetaData.getColumnCount(); + List headerList = getRsHeader(rs); + ObjectMapper mapper = new ObjectMapper(); + mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + while (rs.next()) { + Map 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) { diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/SqlUtils.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/SqlUtils.java index bf58ca34..851dc75b 100644 --- a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/SqlUtils.java +++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/SqlUtils.java @@ -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())) {