mirror of
https://github.com/CodePhiliaX/Chat2DB.git
synced 2025-08-01 08:52:11 +08:00
Merge branch 'dev' into fix/645
This commit is contained in:
@ -74,5 +74,10 @@
|
||||
<artifactId>jsqlparser</artifactId>
|
||||
<version>4.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.locationtech.jts</groupId>
|
||||
<artifactId>jts-core</artifactId>
|
||||
<version>1.19.0</version> <!-- 确保使用最新的版本 -->
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@ -206,4 +206,12 @@ public interface MetaData {
|
||||
*/
|
||||
String getMetaDataName(String ...names);
|
||||
|
||||
|
||||
/**
|
||||
* Get column builder.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
ValueHandler getValueHandler();
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package ai.chat2db.spi;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public interface ValueHandler {
|
||||
|
||||
/**
|
||||
* 处理结果集中的列值
|
||||
* @param rs
|
||||
* @param index
|
||||
* @param limitSize
|
||||
* @return
|
||||
* @throws SQLException
|
||||
*/
|
||||
String getString(ResultSet rs, int index, boolean limitSize)throws SQLException;
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
|
||||
package ai.chat2db.spi.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -9,7 +9,6 @@ import java.util.List;
|
||||
* @author jipengfei
|
||||
* @version : DBConfig.java
|
||||
*/
|
||||
@Data
|
||||
public class DBConfig {
|
||||
|
||||
/**
|
||||
@ -32,7 +31,6 @@ public class DBConfig {
|
||||
*/
|
||||
private List<DriverConfig> driverConfigList;
|
||||
|
||||
|
||||
/**
|
||||
* 建表语句
|
||||
*/
|
||||
@ -42,4 +40,73 @@ public class DBConfig {
|
||||
* 修改表结构
|
||||
*/
|
||||
private String simpleAlterTable;
|
||||
|
||||
|
||||
public String getDbType() {
|
||||
return dbType;
|
||||
}
|
||||
|
||||
public void setDbType(String dbType) {
|
||||
this.dbType = dbType;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public DriverConfig getDefaultDriverConfig() {
|
||||
if (this.defaultDriverConfig != null) {
|
||||
return this.defaultDriverConfig;
|
||||
} else {
|
||||
if (!CollectionUtils.isEmpty(driverConfigList)) {
|
||||
for (DriverConfig driverConfig : driverConfigList) {
|
||||
if (driverConfig.isDefaultDriver()) {
|
||||
return driverConfig;
|
||||
}
|
||||
}
|
||||
return driverConfigList.get(0);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setDefaultDriverConfig(DriverConfig defaultDriverConfig) {
|
||||
this.defaultDriverConfig = defaultDriverConfig;
|
||||
}
|
||||
|
||||
public List<DriverConfig> getDriverConfigList() {
|
||||
return driverConfigList;
|
||||
}
|
||||
|
||||
public void setDriverConfigList(List<DriverConfig> driverConfigList) {
|
||||
this.driverConfigList = driverConfigList;
|
||||
if (!CollectionUtils.isEmpty(driverConfigList)) {
|
||||
for (DriverConfig driverConfig : driverConfigList) {
|
||||
if (driverConfig.isDefaultDriver()) {
|
||||
this.defaultDriverConfig = driverConfig;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getSimpleCreateTable() {
|
||||
return simpleCreateTable;
|
||||
}
|
||||
|
||||
public void setSimpleCreateTable(String simpleCreateTable) {
|
||||
this.simpleCreateTable = simpleCreateTable;
|
||||
}
|
||||
|
||||
public String getSimpleAlterTable() {
|
||||
return simpleAlterTable;
|
||||
}
|
||||
|
||||
public void setSimpleAlterTable(String simpleAlterTable) {
|
||||
this.simpleAlterTable = simpleAlterTable;
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package ai.chat2db.spi.config;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ai.chat2db.spi.model.KeyValue;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@ -12,6 +13,11 @@ import org.apache.commons.lang3.StringUtils;
|
||||
*/
|
||||
@Data
|
||||
public class DriverConfig {
|
||||
|
||||
/**
|
||||
* url
|
||||
*/
|
||||
private String url;
|
||||
/**
|
||||
* jdbcDriver
|
||||
*/
|
||||
@ -22,17 +28,14 @@ public class DriverConfig {
|
||||
*/
|
||||
private String jdbcDriverClass;
|
||||
|
||||
///**
|
||||
// * name
|
||||
// */
|
||||
//private String name;
|
||||
|
||||
/**
|
||||
* downloadJdbcDriverUrls
|
||||
*/
|
||||
private List<String> downloadJdbcDriverUrls;
|
||||
|
||||
|
||||
/**
|
||||
* dbType
|
||||
*/
|
||||
private String dbType;
|
||||
|
||||
/**
|
||||
@ -40,6 +43,13 @@ public class DriverConfig {
|
||||
*/
|
||||
private boolean custom;
|
||||
|
||||
/**
|
||||
* properties
|
||||
*/
|
||||
private List<KeyValue> extendInfo;
|
||||
|
||||
|
||||
private boolean defaultDriver;
|
||||
|
||||
public boolean notEmpty() {
|
||||
return StringUtils.isNotBlank(getJdbcDriver()) && StringUtils.isNotBlank(
|
||||
|
@ -8,6 +8,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
import ai.chat2db.spi.MetaData;
|
||||
import ai.chat2db.spi.SqlBuilder;
|
||||
import ai.chat2db.spi.ValueHandler;
|
||||
import ai.chat2db.spi.model.*;
|
||||
import ai.chat2db.spi.sql.SQLExecutor;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
@ -25,7 +26,15 @@ public class DefaultMetaService implements MetaData {
|
||||
|
||||
@Override
|
||||
public List<Schema> schemas(Connection connection, String databaseName) {
|
||||
return SQLExecutor.getInstance().schemas(connection, databaseName, null);
|
||||
List<Schema> schemas = SQLExecutor.getInstance().schemas(connection, databaseName, null);
|
||||
if(StringUtils.isNotBlank(databaseName) && CollectionUtils.isNotEmpty(schemas)){
|
||||
for ( Schema schema : schemas) {
|
||||
if(StringUtils.isBlank(schema.getDatabaseName())){
|
||||
schema.setDatabaseName(databaseName);
|
||||
}
|
||||
}
|
||||
}
|
||||
return schemas;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -123,6 +132,8 @@ public class DefaultMetaService implements MetaData {
|
||||
return Arrays.stream(names).filter(name -> StringUtils.isNotBlank(name)).collect(Collectors.joining("."));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public ValueHandler getValueHandler() {
|
||||
return new DefaultValueHandler();
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package ai.chat2db.spi.jdbc;
|
||||
|
||||
import ai.chat2db.server.tools.common.util.I18nUtils;
|
||||
import ai.chat2db.spi.ValueHandler;
|
||||
import cn.hutool.core.io.unit.DataSizeUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Blob;
|
||||
import java.sql.Clob;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
@Slf4j
|
||||
public class DefaultValueHandler implements ValueHandler {
|
||||
|
||||
private static final long MAX_RESULT_SIZE = 256 * 1024;
|
||||
|
||||
@Override
|
||||
public String getString(ResultSet rs, int index, boolean limitSize) throws SQLException {
|
||||
Object obj = rs.getObject(index);
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
private 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;
|
||||
}
|
||||
|
||||
if (result.length() > MAX_RESULT_SIZE) {
|
||||
return "[ " + DataSizeUtil.format(MAX_RESULT_SIZE) + " of " + DataSizeUtil.format(result.length()) + " ,"
|
||||
+ I18nUtils.getMessage("execute.exportCsv") + " ] " + result.substring(0,
|
||||
Math.toIntExact(MAX_RESULT_SIZE));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -23,7 +23,17 @@ public class KeyValue implements Serializable {
|
||||
/**
|
||||
* 属性值
|
||||
*/
|
||||
private Object value;
|
||||
private String value;
|
||||
|
||||
/**
|
||||
* 是否必填
|
||||
*/
|
||||
private boolean required;
|
||||
|
||||
/**
|
||||
* 选项
|
||||
*/
|
||||
private List<String> choices;
|
||||
|
||||
public static Map<String, Object> toMap(List<KeyValue> keyValues) {
|
||||
if (CollectionUtils.isEmpty(keyValues)) {
|
||||
|
@ -24,12 +24,12 @@ public class Schema implements Serializable {
|
||||
/**
|
||||
* databaseName
|
||||
*/
|
||||
@JsonAlias({"TABLE_CATALOG"})
|
||||
@JsonAlias({"TABLE_CATALOG","table_catalog"})
|
||||
private String databaseName;
|
||||
/**
|
||||
* 数据名字
|
||||
*/
|
||||
@JsonAlias({"TABLE_SCHEM"})
|
||||
@JsonAlias({"TABLE_SCHEM","table_schem"})
|
||||
private String name;
|
||||
|
||||
|
||||
|
@ -16,6 +16,7 @@ import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.sql.Connection;
|
||||
import java.sql.Driver;
|
||||
import java.sql.DriverPropertyInfo;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@ -40,7 +41,7 @@ public class IDriverManager {
|
||||
}
|
||||
|
||||
public static Connection getConnection(String url, String user, String password, DriverConfig driver)
|
||||
throws SQLException {
|
||||
throws SQLException {
|
||||
Properties info = new Properties();
|
||||
if (user != null) {
|
||||
info.put("user", user);
|
||||
@ -54,8 +55,8 @@ public class IDriverManager {
|
||||
}
|
||||
|
||||
public static Connection getConnection(String url, String user, String password, DriverConfig driver,
|
||||
Map<String, Object> properties)
|
||||
throws SQLException {
|
||||
Map<String, Object> properties)
|
||||
throws SQLException {
|
||||
Properties info = new Properties();
|
||||
if (StringUtils.isNotEmpty(user)) {
|
||||
info.put("user", user);
|
||||
@ -88,6 +89,7 @@ public class IDriverManager {
|
||||
try (Connection connection = driverEntry.getDriver().connect(url, info)) {
|
||||
if (Objects.isNull(connection)) {
|
||||
throw new SQLException(String.format("driver.connect return null , No suitable driver found for url %s", url), SQL_STATE_CODE);
|
||||
|
||||
}
|
||||
return connection;
|
||||
} catch (SQLException sqlException) {
|
||||
@ -102,8 +104,26 @@ public class IDriverManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static DriverPropertyInfo[] getProperty(DriverConfig driver)
|
||||
throws SQLException {
|
||||
if (driver == null) {
|
||||
return null;
|
||||
}
|
||||
DriverEntry driverEntry = DRIVER_ENTRY_MAP.get(driver.getJdbcDriver());
|
||||
if (driverEntry == null) {
|
||||
driverEntry = getJDBCDriver(driver);
|
||||
}
|
||||
try {
|
||||
String url = driver.getUrl() == null ? "" : driver.getUrl();
|
||||
return driverEntry.getDriver().getPropertyInfo(url, null);
|
||||
} catch (Exception var7) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static Connection tryConnectionAgain(DriverEntry driverEntry, String url,
|
||||
Properties info) throws SQLException {
|
||||
Properties info) throws SQLException {
|
||||
if (url.contains("mysql")) {
|
||||
if (!info.containsKey("useSSL")) {
|
||||
info.put("useSSL", "false");
|
||||
@ -114,14 +134,14 @@ public class IDriverManager {
|
||||
}
|
||||
|
||||
private static DriverEntry getJDBCDriver(DriverConfig driver)
|
||||
throws SQLException {
|
||||
throws SQLException {
|
||||
synchronized (driver) {
|
||||
try {
|
||||
if (DRIVER_ENTRY_MAP.containsKey(driver.getJdbcDriver())) {
|
||||
return DRIVER_ENTRY_MAP.get(driver.getJdbcDriver());
|
||||
}
|
||||
ClassLoader cl = getClassLoader(driver);
|
||||
Driver d = (Driver)cl.loadClass(driver.getJdbcDriverClass()).newInstance();
|
||||
Driver d = (Driver) cl.loadClass(driver.getJdbcDriverClass()).newInstance();
|
||||
DriverEntry driverEntry = DriverEntry.builder().driverConfig(driver).driver(d).build();
|
||||
DRIVER_ENTRY_MAP.put(driver.getJdbcDriver(), driverEntry);
|
||||
return driverEntry;
|
||||
|
@ -12,14 +12,16 @@ 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.ValueHandler;
|
||||
import ai.chat2db.spi.jdbc.DefaultValueHandler;
|
||||
import ai.chat2db.spi.model.*;
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.jdbc.support.JdbcUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@ -92,12 +94,12 @@ public class SQLExecutor {
|
||||
}
|
||||
|
||||
public void executeSql(Connection connection, String sql, Consumer<List<Header>> headerConsumer,
|
||||
Consumer<List<String>> rowConsumer) {
|
||||
executeSql(connection, sql, headerConsumer, rowConsumer, true);
|
||||
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) {
|
||||
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();) {
|
||||
@ -115,7 +117,7 @@ public class SQLExecutor {
|
||||
List<Header> headerList = Lists.newArrayListWithExpectedSize(col);
|
||||
for (int i = 1; i <= col; i++) {
|
||||
headerList.add(Header.builder()
|
||||
.dataType(ai.chat2db.spi.util.JdbcUtils.resolveDataType(
|
||||
.dataType(JdbcUtils.resolveDataType(
|
||||
resultSetMetaData.getColumnTypeName(i), resultSetMetaData.getColumnType(i)).getCode())
|
||||
.name(ResultSetUtils.getColumnName(resultSetMetaData, i))
|
||||
.build());
|
||||
@ -125,7 +127,7 @@ public class SQLExecutor {
|
||||
while (rs.next()) {
|
||||
List<String> row = Lists.newArrayListWithExpectedSize(col);
|
||||
for (int i = 1; i <= col; i++) {
|
||||
row.add(ai.chat2db.spi.util.JdbcUtils.getResultSetValue(rs, i, limitSize));
|
||||
row.add(valueHandler.getString(rs, i, limitSize));
|
||||
}
|
||||
rowConsumer.accept(row);
|
||||
}
|
||||
@ -145,8 +147,8 @@ public class SQLExecutor {
|
||||
* @return
|
||||
* @throws SQLException
|
||||
*/
|
||||
public ExecuteResult execute(final String sql, Connection connection) throws SQLException {
|
||||
return execute(sql, connection, true, null, null);
|
||||
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)
|
||||
@ -160,7 +162,7 @@ public class SQLExecutor {
|
||||
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.");
|
||||
connection.rollback();
|
||||
// connection.rollback();
|
||||
}
|
||||
}
|
||||
return executeResult;
|
||||
@ -178,11 +180,10 @@ public class SQLExecutor {
|
||||
* @throws SQLException
|
||||
*/
|
||||
public ExecuteResult execute(final String sql, Connection connection, boolean limitRowSize, Integer offset,
|
||||
Integer count)
|
||||
Integer count, ValueHandler valueHandler)
|
||||
throws SQLException {
|
||||
Assert.notNull(sql, "SQL must not be null");
|
||||
log.info("execute:{}", sql);
|
||||
|
||||
ExecuteResult executeResult = ExecuteResult.builder().sql(sql).success(Boolean.TRUE).build();
|
||||
try (Statement stmt = connection.createStatement()) {
|
||||
stmt.setFetchSize(EasyToolsConstant.MAX_PAGE_SIZE);
|
||||
@ -214,7 +215,7 @@ public class SQLExecutor {
|
||||
chat2dbAutoRowIdIndex = i;
|
||||
continue;
|
||||
}
|
||||
String dataType = ai.chat2db.spi.util.JdbcUtils.resolveDataType(
|
||||
String dataType = JdbcUtils.resolveDataType(
|
||||
resultSetMetaData.getColumnTypeName(i), resultSetMetaData.getColumnType(i)).getCode();
|
||||
headerList.add(Header.builder()
|
||||
.dataType(dataType)
|
||||
@ -241,7 +242,7 @@ public class SQLExecutor {
|
||||
if (chat2dbAutoRowIdIndex == i) {
|
||||
continue;
|
||||
}
|
||||
row.add(ai.chat2db.spi.util.JdbcUtils.getResultSetValue(rs, i, limitRowSize));
|
||||
row.add(valueHandler.getString(rs, i, limitRowSize));
|
||||
}
|
||||
if (count != null && count > 0 && rowCount++ >= count) {
|
||||
break;
|
||||
@ -260,6 +261,7 @@ public class SQLExecutor {
|
||||
return executeResult;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 执行sql
|
||||
*
|
||||
@ -268,8 +270,12 @@ public class SQLExecutor {
|
||||
* @return
|
||||
* @throws SQLException
|
||||
*/
|
||||
public ExecuteResult execute(Connection connection, String sql,ValueHandler valueHandler) throws SQLException {
|
||||
return execute(sql, connection, true, null, null,valueHandler);
|
||||
}
|
||||
|
||||
public ExecuteResult execute(Connection connection, String sql) throws SQLException {
|
||||
return execute(sql, connection, true, null, null);
|
||||
return execute(sql, connection, true, null, null,new DefaultValueHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -455,7 +461,6 @@ public class SQLExecutor {
|
||||
return dbVersion;
|
||||
} catch (Exception e) {
|
||||
log.error("get db version error", e);
|
||||
//throw new RuntimeException(e);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
package ai.chat2db.spi.util;
|
||||
|
||||
import ai.chat2db.spi.config.DBConfig;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class FileUtils {
|
||||
|
||||
public static <T> T readJsonValue(Class<?> loaderClass, String path, Class<T> clazz) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
T value = null;
|
||||
try {
|
||||
value = mapper.readValue(loaderClass.getResourceAsStream(path), clazz);
|
||||
// 使用obj中的数据
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
@ -1,27 +1,25 @@
|
||||
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;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.Map;
|
||||
import java.sql.*;
|
||||
import java.text.Collator;
|
||||
import java.util.*;
|
||||
|
||||
import ai.chat2db.spi.model.KeyValue;
|
||||
import com.alibaba.druid.DbType;
|
||||
|
||||
import ai.chat2db.server.tools.common.util.I18nUtils;
|
||||
import ai.chat2db.spi.config.DriverConfig;
|
||||
import ai.chat2db.spi.enums.DataTypeEnum;
|
||||
import ai.chat2db.spi.model.DataSourceConnect;
|
||||
import ai.chat2db.spi.model.SSHInfo;
|
||||
import ai.chat2db.spi.sql.IDriverManager;
|
||||
import ai.chat2db.spi.ssh.SSHManager;
|
||||
import cn.hutool.core.io.unit.DataSizeUtil;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.jcraft.jsch.Session;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* jdbc工具类
|
||||
@ -132,77 +130,6 @@ public class JdbcUtils {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个返回值
|
||||
*
|
||||
* @param rs
|
||||
* @param index
|
||||
* @return
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static String getResultSetValue(ResultSet rs, int index, boolean limitSize) throws SQLException {
|
||||
Object obj = rs.getObject(index);
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (result.length() > MAX_RESULT_SIZE) {
|
||||
return "[ " + DataSizeUtil.format(MAX_RESULT_SIZE) + " of " + DataSizeUtil.format(result.length()) + " ,"
|
||||
+ I18nUtils.getMessage("execute.exportCsv") + " ] " + result.substring(0,
|
||||
Math.toIntExact(MAX_RESULT_SIZE));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试数据库连接
|
||||
*
|
||||
@ -213,11 +140,11 @@ public class JdbcUtils {
|
||||
* @return
|
||||
*/
|
||||
public static DataSourceConnect testConnect(String url, String host, String port,
|
||||
String userName, String password, String dbType,
|
||||
DriverConfig driverConfig, SSHInfo ssh, Map<String, Object> properties) {
|
||||
String userName, String password, String dbType,
|
||||
DriverConfig driverConfig, SSHInfo ssh, Map<String, Object> properties) {
|
||||
DataSourceConnect dataSourceConnect = DataSourceConnect.builder()
|
||||
.success(Boolean.TRUE)
|
||||
.build();
|
||||
.success(Boolean.TRUE)
|
||||
.build();
|
||||
Session session = null;
|
||||
Connection connection = null;
|
||||
// 加载驱动
|
||||
@ -230,7 +157,7 @@ public class JdbcUtils {
|
||||
}
|
||||
// 创建连接
|
||||
connection = IDriverManager.getConnection(url, userName, password,
|
||||
driverConfig, properties);
|
||||
driverConfig, properties);
|
||||
} catch (Exception e) {
|
||||
log.error("connection fail:", e);
|
||||
dataSourceConnect.setSuccess(Boolean.FALSE);
|
||||
@ -265,4 +192,109 @@ public class JdbcUtils {
|
||||
return dataSourceConnect;
|
||||
}
|
||||
|
||||
public static void closeResultSet(@Nullable ResultSet rs) {
|
||||
if (rs != null) {
|
||||
try {
|
||||
rs.close();
|
||||
} catch (SQLException var2) {
|
||||
log.trace("Could not close JDBC ResultSet", var2);
|
||||
} catch (Throwable var3) {
|
||||
log.trace("Unexpected exception on closing JDBC ResultSet", var3);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void setDriverDefaultProperty(DriverConfig driverConfig) {
|
||||
if(driverConfig == null){
|
||||
return;
|
||||
}
|
||||
List<KeyValue> defaultKeyValues = driverConfig.getExtendInfo();
|
||||
Map<String, KeyValue> valueMap = Maps.newHashMap();
|
||||
if (!CollectionUtils.isEmpty(defaultKeyValues)) {
|
||||
for (KeyValue keyValue : defaultKeyValues) {
|
||||
if (keyValue == null || StringUtils.isBlank(keyValue.getKey())) {
|
||||
continue;
|
||||
}
|
||||
valueMap.put(keyValue.getKey(), keyValue);
|
||||
}
|
||||
}
|
||||
try {
|
||||
DriverPropertyInfo[] propertyInfos = IDriverManager.getProperty(driverConfig);
|
||||
if (propertyInfos == null) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < propertyInfos.length; i++) {
|
||||
DriverPropertyInfo propertyInfo = propertyInfos[i];
|
||||
if (propertyInfo == null) {
|
||||
continue;
|
||||
}
|
||||
KeyValue keyValue = valueMap.get(propertyInfo.name);
|
||||
if (keyValue != null) {
|
||||
String[] choices = propertyInfo.choices;
|
||||
if (CollectionUtils.isEmpty(keyValue.getChoices()) && choices != null && choices.length > 0) {
|
||||
keyValue.setChoices(Lists.newArrayList(choices));
|
||||
}
|
||||
} else {
|
||||
keyValue = new KeyValue();
|
||||
keyValue.setKey(propertyInfo.name);
|
||||
keyValue.setValue(propertyInfo.value);
|
||||
keyValue.setRequired(propertyInfo.required);
|
||||
String[] choices = propertyInfo.choices;
|
||||
if (choices != null && choices.length > 0) {
|
||||
keyValue.setChoices(Lists.newArrayList(choices));
|
||||
}
|
||||
valueMap.put(keyValue.getKey(), keyValue);
|
||||
}
|
||||
}
|
||||
if (!valueMap.isEmpty()) {
|
||||
Comparator comparator = Collator.getInstance(Locale.ENGLISH);
|
||||
List<KeyValue> result = new ArrayList<>(valueMap.values());
|
||||
Collections.sort(result, (o1, o2) -> comparator.compare(o1.getKey(), o2.getKey()));
|
||||
driverConfig.setExtendInfo(result);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
log.error("get property error:", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void removePropertySameAsDefault(DriverConfig driverConfig) {
|
||||
if(driverConfig == null){
|
||||
return;
|
||||
}
|
||||
List<KeyValue> customValue = driverConfig.getExtendInfo();
|
||||
if (CollectionUtils.isEmpty(customValue)) {
|
||||
return ;
|
||||
}
|
||||
Map<String, String> map = Maps.newHashMap();
|
||||
List<KeyValue> result = new ArrayList<>();
|
||||
try {
|
||||
DriverPropertyInfo[] propertyInfos = IDriverManager.getProperty(driverConfig);
|
||||
if (propertyInfos == null) {
|
||||
return ;
|
||||
}
|
||||
for (int i = 0; i < propertyInfos.length; i++) {
|
||||
DriverPropertyInfo propertyInfo = propertyInfos[i];
|
||||
if (propertyInfo == null) {
|
||||
continue;
|
||||
}
|
||||
map.put(propertyInfo.name, propertyInfo.value);
|
||||
}
|
||||
for (KeyValue keyValue : customValue) {
|
||||
if (keyValue == null || StringUtils.isBlank(keyValue.getKey())) {
|
||||
continue;
|
||||
}
|
||||
String value = map.get(keyValue.getKey());
|
||||
if (!StringUtils.equals(value, keyValue.getValue())) {
|
||||
result.add(keyValue);
|
||||
}
|
||||
}
|
||||
Comparator comparator = Collator.getInstance(Locale.ENGLISH);
|
||||
Collections.sort(result, (o1, o2) -> comparator.compare(o1.getKey(), o2.getKey()));
|
||||
driverConfig.setExtendInfo(result);
|
||||
} catch (SQLException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,11 +9,11 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.MapperFeature;
|
||||
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
|
||||
@ -22,28 +22,6 @@ import static ai.chat2db.spi.util.JdbcUtils.getResultSetValue;
|
||||
public class ResultSetUtils {
|
||||
|
||||
|
||||
public static <T> T toObject(ResultSet rs, Class<T> clazz) {
|
||||
try {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> getRsHeader(ResultSet rs) {
|
||||
try {
|
||||
@ -78,6 +56,7 @@ public class ResultSetUtils {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
|
||||
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
|
||||
while (rs.next()) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
for (int i = 1; i <= col; i++) {
|
||||
|
@ -14,6 +14,7 @@ import com.alibaba.druid.sql.ast.statement.SQLTableSource;
|
||||
import com.alibaba.druid.sql.parser.SQLParserUtils;
|
||||
import net.sf.jsqlparser.expression.BinaryExpression;
|
||||
import net.sf.jsqlparser.expression.Function;
|
||||
import net.sf.jsqlparser.parser.CCJSqlParser;
|
||||
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
|
||||
import net.sf.jsqlparser.statement.Statement;
|
||||
import net.sf.jsqlparser.statement.Statements;
|
||||
@ -35,7 +36,12 @@ public class SqlUtils {
|
||||
|
||||
public static void buildCanEditResult(String sql, DbType dbType, ExecuteResult executeResult) {
|
||||
try {
|
||||
Statement statement = CCJSqlParserUtil.parse(sql);
|
||||
Statement statement ;
|
||||
if (DbType.sqlserver.equals(dbType)) {
|
||||
statement = CCJSqlParserUtil.parse(sql, ccjSqlParser -> ccjSqlParser.withSquareBracketQuotation(true));
|
||||
} else {
|
||||
statement = CCJSqlParserUtil.parse(sql);
|
||||
}
|
||||
if (statement instanceof Select) {
|
||||
Select select = (Select) statement;
|
||||
PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
|
||||
@ -56,7 +62,8 @@ public class SqlUtils {
|
||||
// 检查函数是否为 "COUNT"
|
||||
if ("COUNT".equalsIgnoreCase(function.getName())) {
|
||||
executeResult.setCanEdit(false);
|
||||
return; }
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -68,11 +75,12 @@ public class SqlUtils {
|
||||
sqlSelectStatement.getSelect().getFirstQueryBlock().getFrom());
|
||||
executeResult.setTableName(getMetaDataTableName(sqlExprTableSource.getCatalog(), sqlExprTableSource.getSchema(), sqlExprTableSource.getTableName()));
|
||||
}
|
||||
}else {
|
||||
} else {
|
||||
executeResult.setCanEdit(false);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
executeResult.setCanEdit(false);
|
||||
}
|
||||
}
|
||||
@ -121,13 +129,13 @@ public class SqlUtils {
|
||||
return list;
|
||||
}
|
||||
|
||||
private static final String DEFAULT_VALUE = "CHAT2DB_UPDATE_TABLE_DATA_USER_FILLED_DEFAULT";
|
||||
private static final String DEFAULT_VALUE = "CHAT2DB_UPDATE_TABLE_DATA_USER_FILLED_DEFAULT";
|
||||
|
||||
public static String getSqlValue(String value, String dataType) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if(DEFAULT_VALUE.equals(value)){
|
||||
if (DEFAULT_VALUE.equals(value)) {
|
||||
return "DEFAULT";
|
||||
}
|
||||
DataTypeEnum dataTypeEnum = DataTypeEnum.getByCode(dataType);
|
||||
|
@ -0,0 +1,20 @@
|
||||
package ai.chat2db.spi.util;
|
||||
|
||||
import ai.chat2db.spi.model.Table;
|
||||
import ai.chat2db.spi.model.TableColumn;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
|
||||
public class TableUtils {
|
||||
|
||||
public static TableColumn getTableColumn(Table table,String columnName) {
|
||||
if(table == null || CollectionUtils.isEmpty(table.getColumnList())){
|
||||
return null ;
|
||||
}
|
||||
for (TableColumn tableColumn : table.getColumnList()) {
|
||||
if(tableColumn.getName().equalsIgnoreCase(columnName)){
|
||||
return tableColumn ;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user