merge mongo

This commit is contained in:
shanhexi
2023-11-26 23:46:04 +08:00
37 changed files with 737 additions and 460 deletions

View File

@ -1,8 +1,38 @@
package ai.chat2db.plugin.mongodb;
import java.sql.Connection;
import java.sql.SQLException;
import ai.chat2db.spi.DBManage;
import ai.chat2db.spi.jdbc.DefaultDBManage;
import ai.chat2db.spi.sql.Chat2DBContext;
import ai.chat2db.spi.sql.ConnectInfo;
import ai.chat2db.spi.sql.SQLExecutor;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.util.StringUtils;
public class MongodbManage extends DefaultDBManage implements DBManage {
@Override
public void connectDatabase(Connection connection, String database) {
ConnectInfo connectInfo = Chat2DBContext.getConnectInfo();
if (ObjectUtils.anyNull(connectInfo) || StringUtils.isEmpty(connectInfo.getSchemaName())) {
return;
}
String schemaName = connectInfo.getSchemaName();
if (StringUtils.isEmpty(schemaName)) {
return;
}
try {
SQLExecutor.getInstance().execute(connection, "use " + schemaName + ";");
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void dropTable(Connection connection, String databaseName, String schemaName, String tableName) {
String sql = " db. " + tableName + ".drop();";
SQLExecutor.getInstance().executeSql(connection, sql, resultSet -> null);
}
}

View File

@ -2,18 +2,14 @@ package ai.chat2db.server.domain.core.impl;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import ai.chat2db.server.domain.api.param.*;
import ai.chat2db.server.domain.api.param.operation.OperationLogCreateParam;
import ai.chat2db.server.domain.api.service.OperationLogService;
import ai.chat2db.server.domain.api.service.TableService;
import ai.chat2db.server.domain.core.util.MetaNameUtils;
import ai.chat2db.spi.MetaData;
import ai.chat2db.spi.ValueHandler;
import ai.chat2db.spi.model.*;
import ai.chat2db.spi.sql.ConnectInfo;
import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.PagerUtils;
import com.alibaba.druid.sql.SQLUtils;
@ -21,16 +17,34 @@ import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
import com.alibaba.druid.sql.parser.ParserException;
import ai.chat2db.server.domain.api.param.DlCountParam;
import ai.chat2db.server.domain.api.param.DlExecuteParam;
import ai.chat2db.server.domain.api.param.SelectResultOperation;
import ai.chat2db.server.domain.api.param.TableQueryParam;
import ai.chat2db.server.domain.api.param.UpdateSelectResultParam;
import ai.chat2db.server.domain.api.param.operation.OperationLogCreateParam;
import ai.chat2db.server.domain.api.service.DlTemplateService;
import ai.chat2db.server.domain.api.service.OperationLogService;
import ai.chat2db.server.domain.api.service.TableService;
import ai.chat2db.server.domain.core.util.MetaNameUtils;
import ai.chat2db.server.tools.base.constant.EasyToolsConstant;
import ai.chat2db.server.tools.base.enums.DataSourceTypeEnum;
import ai.chat2db.server.tools.base.excption.BusinessException;
import ai.chat2db.server.tools.base.wrapper.result.DataResult;
import ai.chat2db.server.tools.base.wrapper.result.ListResult;
import ai.chat2db.server.tools.common.util.EasyCollectionUtils;
import ai.chat2db.server.tools.common.util.I18nUtils;
import ai.chat2db.spi.MetaData;
import ai.chat2db.spi.ValueHandler;
import ai.chat2db.spi.enums.DataTypeEnum;
import ai.chat2db.spi.enums.SqlTypeEnum;
import ai.chat2db.spi.model.ExecuteResult;
import ai.chat2db.spi.model.Header;
import ai.chat2db.spi.model.TableColumn;
import ai.chat2db.spi.model.TableIndex;
import ai.chat2db.spi.model.TableIndexColumn;
import ai.chat2db.spi.sql.Chat2DBContext;
import ai.chat2db.spi.sql.ConnectInfo;
import ai.chat2db.spi.sql.SQLExecutor;
import ai.chat2db.spi.util.JdbcUtils;
import ai.chat2db.spi.util.SqlUtils;
@ -71,7 +85,6 @@ public class DlTemplateServiceImpl implements DlTemplateService {
List<String> sqlList = SqlUtils.parse(param.getSql(), dbType);
if (CollectionUtils.isEmpty(sqlList)) {
throw new BusinessException("dataSource.sqlAnalysisError");
}
@ -98,7 +111,7 @@ public class DlTemplateServiceImpl implements DlTemplateService {
dataResult.setSuccess(true);
RemoveSpecialGO(param);
DbType dbType =
JdbcUtils.parse2DruidDbType(Chat2DBContext.getConnectInfo().getDbType());
JdbcUtils.parse2DruidDbType(Chat2DBContext.getConnectInfo().getDbType());
List<String> sqlList = SqlUtils.parse(param.getSql(), dbType);
Connection connection = Chat2DBContext.getConnection();
try {
@ -127,29 +140,34 @@ public class DlTemplateServiceImpl implements DlTemplateService {
param.setSql(sql);
}
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
String type = Chat2DBContext.getConnectInfo().getDbType();
boolean supportDruid = !DataSourceTypeEnum.MONGODB.getCode().equals(type);
// 解析sql分页
SQLStatement sqlStatement;
try {
sqlStatement = SQLUtils.parseSingleStatement(originalSql, dbType);
// 是否需要代码帮忙分页
if (sqlStatement instanceof SQLSelectStatement) {
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();
SQLStatement sqlStatement = null;
if (supportDruid) {
try {
sqlStatement = SQLUtils.parseSingleStatement(originalSql, dbType);
} catch (ParserException e) {
log.warn("解析sql失败:{}", originalSql, e);
}
} catch (ParserException e) {
log.warn("解析sql失败:{}", originalSql, e);
}
// Mongodb is currently unable to recognize it, so every time a page is transmitted
if (!supportDruid || (sqlStatement instanceof SQLSelectStatement)) {
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();
}
ExecuteResult executeResult = null;
if (SqlTypeEnum.SELECT.getCode().equals(sqlType) && !SqlUtils.hasPageLimit(originalSql, dbType)) {
String pageLimit = Chat2DBContext.getSqlBuilder().pageLimit(originalSql, offset, pageNo, pageSize);
@ -161,19 +179,23 @@ public class DlTemplateServiceImpl implements DlTemplateService {
executeResult = execute(originalSql, offset, count);
}
executeResult.setSqlType(sqlType);
executeResult.setOriginalSql(originalSql);
try {
SqlUtils.buildCanEditResult(originalSql, dbType, executeResult);
} catch (Exception e) {
log.warn("buildCanEditResult error", e);
boolean supportJsqlParser = !DataSourceTypeEnum.MONGODB.getCode().equals(type);
if (supportJsqlParser) {
try {
SqlUtils.buildCanEditResult(originalSql, dbType, executeResult);
} catch (Exception e) {
log.warn("buildCanEditResult error", e);
}
}
if (SqlTypeEnum.SELECT.getCode().equals(sqlType)) {
executeResult.setPageNo(pageNo);
executeResult.setPageSize(pageSize);
executeResult.setHasNextPage(
CollectionUtils.size(executeResult.getDataList()) >= executeResult.getPageSize());
CollectionUtils.size(executeResult.getDataList()) >= executeResult.getPageSize());
} else {
executeResult.setPageNo(pageNo);
executeResult.setPageSize(CollectionUtils.size(executeResult.getDataList()));
@ -181,13 +203,14 @@ public class DlTemplateServiceImpl implements DlTemplateService {
}
List<Header> headers = executeResult.getHeaderList();
if (executeResult.getSuccess() && executeResult.isCanEdit() && CollectionUtils.isNotEmpty(headers)){
headers = setColumnInfo(headers, executeResult.getTableName(), param.getSchemaName(), param.getDatabaseName());
if (executeResult.getSuccess() && executeResult.isCanEdit() && CollectionUtils.isNotEmpty(headers)) {
headers = setColumnInfo(headers, executeResult.getTableName(), param.getSchemaName(),
param.getDatabaseName());
}
Header rowNumberHeader = Header.builder()
.name(I18nUtils.getMessage("sqlResult.rowNumber"))
.dataType(DataTypeEnum.CHAT2DB_ROW_NUMBER
.getCode()).build();
.name(I18nUtils.getMessage("sqlResult.rowNumber"))
.dataType(DataTypeEnum.CHAT2DB_ROW_NUMBER
.getCode()).build();
executeResult.setHeaderList(EasyCollectionUtils.union(Arrays.asList(rowNumberHeader), headers));
if (executeResult.getDataList() != null) {
@ -223,7 +246,7 @@ public class DlTemplateServiceImpl implements DlTemplateService {
return DataResult.of(0L);
}
DbType dbType =
JdbcUtils.parse2DruidDbType(Chat2DBContext.getConnectInfo().getDbType());
JdbcUtils.parse2DruidDbType(Chat2DBContext.getConnectInfo().getDbType());
String sql = param.getSql();
// 解析sql分页
SQLStatement sqlStatement = SQLUtils.parseSingleStatement(sql, dbType);
@ -238,11 +261,11 @@ public class DlTemplateServiceImpl implements DlTemplateService {
return DataResult.of(0L);
}
String count = EasyCollectionUtils.stream(executeResult.getDataList())
.findFirst()
.orElse(Collections.emptyList())
.stream()
.findFirst()
.orElse("0");
.findFirst()
.orElse(Collections.emptyList())
.stream()
.findFirst()
.orElse("0");
return DataResult.of(Long.valueOf(count));
}
@ -285,7 +308,8 @@ public class DlTemplateServiceImpl implements DlTemplateService {
return keyColumns;
}
private String getDeleteSql(UpdateSelectResultParam param, List<String> row, MetaData metaSchema, List<String> keyColumns) {
private String getDeleteSql(UpdateSelectResultParam param, List<String> row, MetaData metaSchema,
List<String> keyColumns) {
StringBuilder script = new StringBuilder();
script.append("DELETE FROM ").append(param.getTableName()).append("");
@ -303,12 +327,12 @@ public class DlTemplateServiceImpl implements DlTemplateService {
String value = SqlUtils.getSqlValue(oldValue, header.getDataType());
if (value == null) {
script.append(metaSchema.getMetaDataName(header.getName()))
.append(" is null and ");
.append(" is null and ");
} else {
script.append(metaSchema.getMetaDataName(header.getName()))
.append(" = ")
.append(value)
.append(" and ");
.append(" = ")
.append(value)
.append(" and ");
}
}
} else {
@ -320,12 +344,12 @@ public class DlTemplateServiceImpl implements DlTemplateService {
String value = SqlUtils.getSqlValue(oldValue, header.getDataType());
if (value == null) {
script.append(metaSchema.getMetaDataName(columnName))
.append(" is null and ");
.append(" is null and ");
} else {
script.append(metaSchema.getMetaDataName(columnName))
.append(" = ")
.append(value)
.append(" and ");
.append(" = ")
.append(value)
.append(" and ");
}
}
}
@ -340,23 +364,23 @@ public class DlTemplateServiceImpl implements DlTemplateService {
}
StringBuilder script = new StringBuilder();
script.append("INSERT INTO ").append(param.getTableName())
.append(" (");
.append(" (");
for (int i = 1; i < row.size(); i++) {
Header header = param.getHeaderList().get(i);
//String newValue = row.get(i);
//if (newValue != null) {
script.append(metaSchema.getMetaDataName(header.getName()))
.append(",");
// }
script.append(metaSchema.getMetaDataName(header.getName()))
.append(",");
// }
}
script.deleteCharAt(script.length() - 1);
script.append(") VALUES (");
for (int i = 1; i < row.size(); i++) {
String newValue = row.get(i);
//if (newValue != null) {
Header header = param.getHeaderList().get(i);
script.append(SqlUtils.getSqlValue(newValue, header.getDataType()))
.append(",");
Header header = param.getHeaderList().get(i);
script.append(SqlUtils.getSqlValue(newValue, header.getDataType()))
.append(",");
//}
}
script.deleteCharAt(script.length() - 1);
@ -365,9 +389,9 @@ public class DlTemplateServiceImpl implements DlTemplateService {
}
private String getUpdateSql(UpdateSelectResultParam param, List<String> row, List<String> odlRow, MetaData metaSchema,
List<String> keyColumns, boolean copy) {
private String getUpdateSql(UpdateSelectResultParam param, List<String> row, List<String> odlRow,
MetaData metaSchema,
List<String> keyColumns, boolean copy) {
StringBuilder script = new StringBuilder();
if (CollectionUtils.isEmpty(row) || CollectionUtils.isEmpty(odlRow)) {
return "";
@ -382,9 +406,9 @@ public class DlTemplateServiceImpl implements DlTemplateService {
Header header = param.getHeaderList().get(i);
String newSqlValue = SqlUtils.getSqlValue(newValue, header.getDataType());
script.append(metaSchema.getMetaDataName(header.getName()))
.append(" = ")
.append(newSqlValue)
.append(",");
.append(" = ")
.append(newSqlValue)
.append(",");
}
script.deleteCharAt(script.length() - 1);
script.append(buildWhere(param.getHeaderList(), odlRow, metaSchema, keyColumns));
@ -402,7 +426,8 @@ public class DlTemplateServiceImpl implements DlTemplateService {
if (CollectionUtils.isEmpty(columns)) {
return headers;
}
Map<String, TableColumn> columnMap = columns.stream().collect(Collectors.toMap(TableColumn::getName, tableColumn -> tableColumn));
Map<String, TableColumn> columnMap = columns.stream().collect(
Collectors.toMap(TableColumn::getName, tableColumn -> tableColumn));
List<TableIndex> tableIndices = tableService.queryIndexes(tableQueryParam);
if (!CollectionUtils.isEmpty(tableIndices)) {
for (TableIndex tableIndex : tableIndices) {
@ -431,8 +456,8 @@ public class DlTemplateServiceImpl implements DlTemplateService {
}
}
}catch (Exception e){
log.error("setColumnInfo error:",e);
} catch (Exception e) {
log.error("setColumnInfo error:", e);
}
return headers;
}
@ -441,14 +466,15 @@ public class DlTemplateServiceImpl implements DlTemplateService {
ExecuteResult executeResult;
try {
ValueHandler valueHandler = Chat2DBContext.getMetaData().getValueHandler();
executeResult = SQLExecutor.getInstance().execute(sql, Chat2DBContext.getConnection(), true, offset, count,valueHandler);
executeResult = SQLExecutor.getInstance().execute(sql, Chat2DBContext.getConnection(), true, offset, count,
valueHandler);
} catch (SQLException e) {
log.warn("执行sql:{}异常", sql, e);
executeResult = ExecuteResult.builder()
.sql(sql)
.success(Boolean.FALSE)
.message(e.getMessage())
.build();
.sql(sql)
.success(Boolean.FALSE)
.message(e.getMessage())
.build();
}
return executeResult;
}
@ -467,7 +493,8 @@ public class DlTemplateServiceImpl implements DlTemplateService {
createParam.setSchemaName(connectInfo.getSchemaName());
createParam.setUseTime(executeResult.getDuration());
createParam.setType(connectInfo.getDbType());
createParam.setOperationRows(executeResult.getUpdateCount() != null ? Long.valueOf(executeResult.getUpdateCount()) : null);
createParam.setOperationRows(
executeResult.getUpdateCount() != null ? Long.valueOf(executeResult.getUpdateCount()) : null);
operationLogService.create(createParam);
} catch (Exception e) {
log.error("addOperationLog error:", e);

View File

@ -20,6 +20,12 @@ public enum DataSourceTypeEnum implements BaseEnum<String> {
*/
REDIS("redis数据库连接"),
/**
* mongo数据库连接
*/
MONGODB("mongo数据库连接"),
;
final String description;

View File

@ -6,12 +6,14 @@ import java.util.List;
import ai.chat2db.server.domain.api.param.DlExecuteParam;
import ai.chat2db.server.domain.api.param.UpdateSelectResultParam;
import ai.chat2db.server.domain.api.service.DlTemplateService;
import ai.chat2db.server.tools.base.enums.DataSourceTypeEnum;
import ai.chat2db.server.tools.base.wrapper.result.DataResult;
import ai.chat2db.server.tools.base.wrapper.result.ListResult;
import ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;
import ai.chat2db.server.web.api.controller.rdb.converter.RdbWebConverter;
import ai.chat2db.server.web.api.controller.rdb.request.DdlCountRequest;
import ai.chat2db.server.web.api.controller.rdb.request.DmlRequest;
import ai.chat2db.server.web.api.controller.rdb.request.DmlTableRequest;
import ai.chat2db.server.web.api.controller.rdb.request.SelectResultUpdateRequest;
import ai.chat2db.server.web.api.controller.rdb.vo.ExecuteResultVO;
import ai.chat2db.spi.model.ExecuteResult;
@ -55,6 +57,25 @@ public class RdbDmlController {
return ListResult.of(resultVOS);
}
/**
* 查询表结构信息
*
* @param request
* @return
*/
@RequestMapping(value = "/execute_table", method = {RequestMethod.POST, RequestMethod.PUT})
public ListResult<ExecuteResultVO> executeTable(@RequestBody DmlTableRequest request) {
DlExecuteParam param = rdbWebConverter.request2param(request);
// 解析sql
String type = Chat2DBContext.getConnectInfo().getDbType();
if (DataSourceTypeEnum.MONGODB.getCode().equals(type)) {
param.setSql("db." + request.getTableName() + ".find()");
} else {
param.setSql("select * from " + request.getTableName());
}
return dlTemplateService.execute(param)
.map(rdbWebConverter::dto2vo);
}
/**
* update 查询结果
@ -63,22 +84,22 @@ public class RdbDmlController {
* @return
*/
@RequestMapping(value = "/execute_update", method = {RequestMethod.POST, RequestMethod.PUT})
public DataResult<ExecuteResultVO> executeSelectResultUpdate(@RequestBody DmlRequest request) {
public DataResult<ExecuteResultVO> executeSelectResultUpdate(@RequestBody DmlRequest request) {
DlExecuteParam param = rdbWebConverter.request2param(request);
DataResult<ExecuteResult> result = dlTemplateService.executeUpdate(param);
if(!result.success()){
return DataResult.error(result.getErrorCode(),result.getErrorMessage());
DataResult<ExecuteResult> result = dlTemplateService.executeUpdate(param);
if (!result.success()) {
return DataResult.error(result.getErrorCode(), result.getErrorMessage());
}
return DataResult.of(rdbWebConverter.dto2vo(result.getData()));
return DataResult.of(rdbWebConverter.dto2vo(result.getData()));
}
@RequestMapping(value = "/get_update_sql", method = {RequestMethod.POST, RequestMethod.PUT})
public DataResult<String> getUpdateSelectResultSql(@RequestBody SelectResultUpdateRequest request) {
UpdateSelectResultParam param = rdbWebConverter.request2param(request);
return dlTemplateService.updateSelectResult(param);
}
/**
* 增删改查等数据运维
*
@ -109,7 +130,7 @@ public class RdbDmlController {
if (flag) {
//connection.commit();
return DataResult.of(resultVOS.get(0));
}else {
} else {
//connection.rollback();
return DataResult.of(executeResult);
}

View File

@ -7,7 +7,6 @@ import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.List;
import ai.chat2db.spi.jdbc.DefaultValueHandler;
import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.SQLUtils.FormatOption;
@ -28,11 +27,11 @@ import ai.chat2db.server.domain.api.enums.ExportSizeEnum;
import ai.chat2db.server.domain.api.enums.ExportTypeEnum;
import ai.chat2db.server.tools.base.excption.BusinessException;
import ai.chat2db.server.tools.common.exception.ParamBusinessException;
import ai.chat2db.server.tools.common.util.ConfigUtils;
import ai.chat2db.server.tools.common.util.EasyCollectionUtils;
import ai.chat2db.server.tools.common.util.EasyEnumUtils;
import ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;
import ai.chat2db.server.web.api.controller.rdb.request.DataExportRequest;
import ai.chat2db.spi.jdbc.DefaultValueHandler;
import ai.chat2db.spi.sql.Chat2DBContext;
import ai.chat2db.spi.sql.SQLExecutor;
import ai.chat2db.spi.util.JdbcUtils;
@ -92,11 +91,17 @@ public class RdbDmlExportController {
throw new ParamBusinessException("exportSize");
}
DbType dbType = JdbcUtils.parse2DruidDbType(Chat2DBContext.getConnectInfo().getDbType());
SQLStatement sqlStatement = SQLUtils.parseSingleStatement(sql, dbType);
if (!(sqlStatement instanceof SQLSelectStatement)) {
throw new BusinessException("dataSource.sqlAnalysisError");
String tableName;
if (dbType != null) {
SQLStatement sqlStatement = SQLUtils.parseSingleStatement(sql, dbType);
if (!(sqlStatement instanceof SQLSelectStatement)) {
throw new BusinessException("dataSource.sqlAnalysisError");
}
tableName = SqlUtils.getTableName(sql, dbType);
} else {
tableName = StringUtils.join(Lists.newArrayList(request.getDatabaseName(), request.getSchemaName()), "_");
}
String tableName = SqlUtils.getTableName(sql, dbType);
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode(
tableName + "_" + LocalDateTime.now().format(DatePattern.PURE_DATETIME_FORMATTER),
@ -108,7 +113,6 @@ public class RdbDmlExportController {
} else {
doExportInsert(sql, response, fileName, dbType, tableName);
}
String SS = ConfigUtils.APP_PATH;
}
private void doExportCsv(String sql, HttpServletResponse response, String fileName)
@ -131,7 +135,7 @@ public class RdbDmlExportController {
List<List<String>> writeDataList = Lists.newArrayList();
writeDataList.add(dataList);
excelWrapper.getExcelWriter().write(writeDataList, excelWrapper.getWriteSheet());
}, false,new DefaultValueHandler());
}, false, new DefaultValueHandler());
} finally {
if (excelWrapper.getExcelWriter() != null) {
excelWrapper.getExcelWriter().finish();
@ -162,7 +166,7 @@ public class RdbDmlExportController {
sqlInsertStatement.setValues(valuesClause);
printWriter.println(SQLUtils.toSQLString(sqlInsertStatement, dbType, INSERT_FORMAT_OPTION) + ";");
}, false,new DefaultValueHandler());
}, false, new DefaultValueHandler());
}
}

View File

@ -41,6 +41,15 @@ public abstract class RdbWebConverter {
*/
public abstract DlExecuteParam request2param(DmlRequest request);
/**
* 参数转换
*
* @param request
* @return
*/
public abstract DlExecuteParam request2param(DmlTableRequest request);
/**
* 参数转换
*

View File

@ -0,0 +1,45 @@
package ai.chat2db.server.web.api.controller.rdb.request;
import ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;
import ai.chat2db.server.web.api.controller.data.source.request.DataSourceConsoleRequestInfo;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* @author moji
* @version TableManageRequest.java, v 0.1 2022年09月16日 17:55 moji Exp $
* @date 2022/09/16
*/
@Data
public class DmlTableRequest extends DataSourceBaseRequest implements DataSourceConsoleRequestInfo {
/**
* 表的名词
*/
@NotNull
private String tableName;
/**
* 控制台id
*/
@NotNull
private Long consoleId;
/**
* 分页编码
* 只有select语句才有
*/
private Integer pageNo;
/**
* 分页大小
* 只有select语句才有
*/
private Integer pageSize;
/**
* 返回全部数据
* 只有select语句才有
*/
private Boolean pageSizeAll;
}

View File

@ -79,5 +79,11 @@
<artifactId>jts-core</artifactId>
<version>1.19.0</version> <!-- 确保使用最新的版本 -->
</dependency>
<!-- https://mvnrepository.com/artifact/org.mongodb/bson -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>bson</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,14 @@
package ai.chat2db.spi.sql;
import org.bson.json.Converter;
import org.bson.json.StrictJsonWriter;
import org.bson.types.ObjectId;
public class MongExtendedJsonObjectIdConverter implements Converter<ObjectId> {
@Override
public void convert(final ObjectId value, final StrictJsonWriter writer) {
writer.writeStartObject();
writer.writeString("", value.toHexString());
writer.writeEndObject();
}
}

View File

@ -1,25 +1,44 @@
package ai.chat2db.spi.sql;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Map;
import java.util.Objects;
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.base.enums.DataSourceTypeEnum;
import ai.chat2db.server.tools.common.util.I18nUtils;
import ai.chat2db.spi.ValueHandler;
import ai.chat2db.spi.jdbc.DefaultValueHandler;
import ai.chat2db.spi.model.*;
import ai.chat2db.spi.model.Database;
import ai.chat2db.spi.model.ExecuteResult;
import ai.chat2db.spi.model.Header;
import ai.chat2db.spi.model.Procedure;
import ai.chat2db.spi.model.Schema;
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.Type;
import ai.chat2db.spi.util.JdbcUtils;
import ai.chat2db.spi.util.ResultSetUtils;
import cn.hutool.core.date.TimeInterval;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.bson.Document;
import org.springframework.util.Assert;
import java.sql.*;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Dbhub 统一数据库连接管理
*
@ -90,12 +109,12 @@ public class SQLExecutor {
}
public void executeSql(Connection connection, String sql, Consumer<List<Header>> headerConsumer,
Consumer<List<String>> rowConsumer, ValueHandler valueHandler) {
Consumer<List<String>> rowConsumer, ValueHandler valueHandler) {
executeSql(connection, sql, headerConsumer, rowConsumer, true, valueHandler);
}
public void executeSql(Connection connection, String sql, Consumer<List<Header>> headerConsumer,
Consumer<List<String>> rowConsumer, boolean limitSize, ValueHandler valueHandler) {
Consumer<List<String>> rowConsumer, boolean limitSize, ValueHandler valueHandler) {
Assert.notNull(sql, "SQL must not be null");
log.info("execute:{}", sql);
try (Statement stmt = connection.createStatement();) {
@ -113,10 +132,10 @@ public class SQLExecutor {
List<Header> headerList = Lists.newArrayListWithExpectedSize(col);
for (int i = 1; i <= col; i++) {
headerList.add(Header.builder()
.dataType(JdbcUtils.resolveDataType(
resultSetMetaData.getColumnTypeName(i), resultSetMetaData.getColumnType(i)).getCode())
.name(ResultSetUtils.getColumnName(resultSetMetaData, i))
.build());
.dataType(JdbcUtils.resolveDataType(
resultSetMetaData.getColumnTypeName(i), resultSetMetaData.getColumnType(i)).getCode())
.name(ResultSetUtils.getColumnName(resultSetMetaData, i))
.build());
}
headerConsumer.accept(headerList);
@ -143,12 +162,13 @@ public class SQLExecutor {
* @return
* @throws SQLException
*/
public ExecuteResult execute(final String sql, Connection connection, ValueHandler valueHandler) throws SQLException {
public ExecuteResult execute(final String sql, Connection connection, ValueHandler valueHandler)
throws SQLException {
return execute(sql, connection, true, null, null, valueHandler);
}
public ExecuteResult executeUpdate(final String sql, Connection connection, int n)
throws SQLException {
throws SQLException {
Assert.notNull(sql, "SQL must not be null");
log.info("execute:{}", sql);
// connection.setAutoCommit(false);
@ -157,7 +177,9 @@ public class SQLExecutor {
int affectedRows = stmt.executeUpdate(sql);
if (affectedRows != n) {
executeResult.setSuccess(false);
executeResult.setMessage("Update error " + sql + " update affectedRows = " + affectedRows + ", Each SQL statement should update no more than one record. Please use a unique key for updates.");
executeResult.setMessage("Update error " + sql + " update affectedRows = " + affectedRows
+ ", Each SQL statement should update no more than one record. Please use a unique key for "
+ "updates.");
// connection.rollback();
}
}
@ -176,14 +198,18 @@ public class SQLExecutor {
* @throws SQLException
*/
public ExecuteResult execute(final String sql, Connection connection, boolean limitRowSize, Integer offset,
Integer count, ValueHandler valueHandler)
throws SQLException {
Integer count, ValueHandler valueHandler)
throws SQLException {
Assert.notNull(sql, "SQL must not be null");
log.info("execute:{}", sql);
String type = Chat2DBContext.getConnectInfo().getDbType();
ExecuteResult executeResult = ExecuteResult.builder().sql(sql).success(Boolean.TRUE).build();
try (Statement stmt = connection.createStatement()) {
stmt.setFetchSize(EasyToolsConstant.MAX_PAGE_SIZE);
//stmt.setQueryTimeout(30);
if (!DataSourceTypeEnum.MONGODB.getCode().equals(type)) {
stmt.setQueryTimeout(30);
}
if (offset != null && count != null) {
stmt.setMaxRows(offset + count);
}
@ -205,24 +231,37 @@ public class SQLExecutor {
executeResult.setHeaderList(headerList);
int chat2dbAutoRowIdIndex = -1;// chat2db自动生成的行分页ID
boolean isMongoMap = false;
for (int i = 1; i <= col; i++) {
String name = ResultSetUtils.getColumnName(resultSetMetaData, i);
// The returned map is from mongodb, and you need to parse the map yourself
if (DataSourceTypeEnum.MONGODB.getCode().equals(type) && i == 1 && "map".equals(name)) {
isMongoMap = true;
break;
}
if ("CAHT2DB_AUTO_ROW_ID".equals(name)) {
chat2dbAutoRowIdIndex = i;
continue;
}
String dataType = JdbcUtils.resolveDataType(
resultSetMetaData.getColumnTypeName(i), resultSetMetaData.getColumnType(i)).getCode();
resultSetMetaData.getColumnTypeName(i), resultSetMetaData.getColumnType(i)).getCode();
headerList.add(Header.builder()
.dataType(dataType)
.name(name)
.build());
.dataType(dataType)
.name(name)
.build());
}
// 获取数据信息
List<List<String>> dataList = Lists.newArrayList();
executeResult.setDataList(dataList);
Map<String, Header> headerListMap = null;
List<Map<String, String>> dataListMap = null;
if (isMongoMap) {
headerListMap = Maps.newLinkedHashMap();
dataListMap = Lists.newArrayList();
}
if (offset == null || offset < 0) {
offset = 0;
}
@ -232,18 +271,53 @@ public class SQLExecutor {
if (rowNumber++ < offset) {
continue;
}
List<String> row = Lists.newArrayListWithExpectedSize(col);
dataList.add(row);
for (int i = 1; i <= col; i++) {
if (chat2dbAutoRowIdIndex == i) {
continue;
if (!isMongoMap) {
List<String> row = Lists.newArrayListWithExpectedSize(col);
dataList.add(row);
for (int i = 1; i <= col; i++) {
if (chat2dbAutoRowIdIndex == i) {
continue;
}
row.add(valueHandler.getString(rs, i, limitRowSize));
}
} else {
for (int i = 1; i <= col; i++) {
Object o = rs.getObject(i);
Map<String, String> row = Maps.newHashMap();
dataListMap.add(row);
if (o instanceof Document document) {
for (String string : document.keySet()) {
headerListMap.computeIfAbsent(string, k -> Header.builder()
.dataType("string")
.name(string)
.build());
row.put(string, Objects.toString(document.get(string)));
}
} else {
headerListMap.computeIfAbsent("_unknown", k -> Header.builder()
.dataType("string")
.name("_unknown")
.build());
row.put("_unknown", Objects.toString(o));
}
}
row.add(valueHandler.getString(rs, i, limitRowSize));
}
if (count != null && count > 0 && rowCount++ >= count) {
break;
}
}
if (isMongoMap) {
headerList.addAll(headerListMap.values().stream().toList());
for (Map<String, String> stringStringMap : dataListMap) {
List<String> dataTempList = Lists.newArrayList();
dataList.add(dataTempList);
for (Header value : headerListMap.values()) {
dataTempList.add(stringStringMap.get(value.getName()));
}
}
}
executeResult.setDuration(timeInterval.interval());
} finally {
JdbcUtils.closeResultSet(rs);
@ -257,7 +331,6 @@ public class SQLExecutor {
return executeResult;
}
/**
* 执行sql
*
@ -337,12 +410,12 @@ public class SQLExecutor {
* @return
*/
public List<Table> tables(Connection connection, String databaseName, String schemaName, String tableName,
String types[]) {
String types[]) {
try {
DatabaseMetaData metadata = connection.getMetaData();
ResultSet resultSet = metadata.getTables(databaseName, schemaName, tableName,
types);
types);
// 如果connection为mysql
if ("MySQL".equalsIgnoreCase(metadata.getDatabaseProductName())) {
// 获取mysql表的comment
@ -382,10 +455,10 @@ public class SQLExecutor {
* @return
*/
public List<TableColumn> columns(Connection connection, String databaseName, String schemaName, String
tableName,
String columnName) {
tableName,
String columnName) {
try (ResultSet resultSet = connection.getMetaData().getColumns(databaseName, schemaName, tableName,
columnName)) {
columnName)) {
return ResultSetUtils.toObjectList(resultSet, TableColumn.class);
} catch (Exception e) {
throw new RuntimeException(e);
@ -404,22 +477,22 @@ public class SQLExecutor {
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)) {
false,
false)) {
List<TableIndexColumn> tableIndexColumns = ResultSetUtils.toObjectList(resultSet, TableIndexColumn.class);
tableIndexColumns.stream().filter(c -> c.getIndexName() != null).collect(
Collectors.groupingBy(TableIndexColumn::getIndexName)).entrySet()
.stream().forEach(entry -> {
TableIndex tableIndex = new TableIndex();
TableIndexColumn column = entry.getValue().get(0);
tableIndex.setName(entry.getKey());
tableIndex.setTableName(column.getTableName());
tableIndex.setSchemaName(column.getSchemaName());
tableIndex.setDatabaseName(column.getDatabaseName());
tableIndex.setUnique(!column.getNonUnique());
tableIndex.setColumnList(entry.getValue());
tableIndices.add(tableIndex);
});
Collectors.groupingBy(TableIndexColumn::getIndexName)).entrySet()
.stream().forEach(entry -> {
TableIndex tableIndex = new TableIndex();
TableIndexColumn column = entry.getValue().get(0);
tableIndex.setName(entry.getKey());
tableIndex.setTableName(column.getTableName());
tableIndex.setSchemaName(column.getSchemaName());
tableIndex.setDatabaseName(column.getDatabaseName());
tableIndex.setUnique(!column.getNonUnique());
tableIndex.setColumnList(entry.getValue());
tableIndices.add(tableIndex);
});
} catch (SQLException e) {
throw new RuntimeException(e);
}
@ -435,7 +508,7 @@ public class SQLExecutor {
* @return List<Function>
*/
public List<ai.chat2db.spi.model.Function> functions(Connection connection, String databaseName,
String schemaName) {
String schemaName) {
try (ResultSet resultSet = connection.getMetaData().getFunctions(databaseName, schemaName, null);) {
return ResultSetUtils.toObjectList(resultSet, ai.chat2db.spi.model.Function.class);
} catch (Exception e) {
@ -443,11 +516,14 @@ public class SQLExecutor {
}
}
/**
* Retrieves a description of all the data types supported by this database. They are ordered by DATA_TYPE and then by how closely the data type maps to the corresponding JDBC SQL type.
* If the database supports SQL distinct types, then getTypeInfo() will return a single row with a TYPE_NAME of DISTINCT and a DATA_TYPE of Types.DISTINCT. If the database supports SQL structured types, then getTypeInfo() will return a single row with a TYPE_NAME of STRUCT and a DATA_TYPE of Types.STRUCT.
* If SQL distinct or structured types are supported, then information on the individual types may be obtained from the getUDTs() method.
* Retrieves a description of all the data types supported by this database. They are ordered by DATA_TYPE and then
* by how closely the data type maps to the corresponding JDBC SQL type.
* If the database supports SQL distinct types, then getTypeInfo() will return a single row with a TYPE_NAME of
* DISTINCT and a DATA_TYPE of Types.DISTINCT. If the database supports SQL structured types, then getTypeInfo()
* will return a single row with a TYPE_NAME of STRUCT and a DATA_TYPE of Types.STRUCT.
* If SQL distinct or structured types are supported, then information on the individual types may be obtained from
* the getUDTs() method.
*
* @param connection connection
* @return List<Function>

View File

View File

@ -296,6 +296,13 @@
<version>4.4.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mongodb/bson -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>bson</artifactId>
<version>4.11.1</version>
</dependency>
</dependencies>
</dependencyManagement>