Supports fuzzy query of table names, not case sensitive.

Automatically add pagination parameters when users query SQL without pagination parameters.
Sort the database schema and prioritize the user-defined ones.
This commit is contained in:
SwallowGG
2023-10-22 22:46:26 +08:00
parent c0161df83b
commit 51a35f770e
28 changed files with 539 additions and 129 deletions

View File

@ -2,12 +2,26 @@ package ai.chat2db.plugin.db2;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import ai.chat2db.plugin.db2.builder.DB2SqlBuilder;
import ai.chat2db.spi.MetaData;
import ai.chat2db.spi.SqlBuilder;
import ai.chat2db.spi.jdbc.DefaultMetaService;
import ai.chat2db.spi.jdbc.DefaultSqlBuilder;
import ai.chat2db.spi.model.Schema;
import ai.chat2db.spi.sql.SQLExecutor;
import ai.chat2db.spi.util.SortUtils;
public class DB2MetaData extends DefaultMetaService implements MetaData {
private List<String> systemSchemas = Arrays.asList("NULLID","SQLJ","SYSCAT","SYSFUN","SYSIBM","SYSIBMADM","SYSIBMINTERNAL","SYSIBMTS","SYSPROC","SYSPUBLIC","SYSSTAT","SYSTOOLS");
@Override
public List<Schema> schemas(Connection connection, String databaseName) {
List<Schema> schemas = SQLExecutor.getInstance().schemas(connection, databaseName, null);
return SortUtils.sortSchema(schemas, systemSchemas);
}
private String functionSQL
= "CREATE FUNCTION tableSchema.ufn_GetCreateTableScript( @schema_name NVARCHAR(128), @table_name NVARCHAR"
+ "(128)) RETURNS NVARCHAR(MAX) AS BEGIN DECLARE @CreateTableScript NVARCHAR(MAX); DECLARE @IndexScripts "
@ -40,7 +54,6 @@ public class DB2MetaData extends DefaultMetaService implements MetaData {
@Override
public String tableDDL(Connection connection, String databaseName, String schemaName, String tableName) {
try {
System.out.println(functionSQL);
SQLExecutor.getInstance().executeSql(connection, functionSQL.replace("tableSchema", schemaName), resultSet -> null);
} catch (Exception e) {
//log.error("创建函数失败", e);
@ -59,6 +72,9 @@ public class DB2MetaData extends DefaultMetaService implements MetaData {
return null;
});
}
@Override
public SqlBuilder getSqlBuilder() {
return new DB2SqlBuilder();
}
}

View File

@ -0,0 +1,23 @@
package ai.chat2db.plugin.db2.builder;
import ai.chat2db.spi.SqlBuilder;
import ai.chat2db.spi.jdbc.DefaultSqlBuilder;
public class DB2SqlBuilder extends DefaultSqlBuilder {
@Override
public String pageLimit(String sql, int offset, int pageNo, int pageSize) {
int startRow = offset + 1;
int endRow = offset+ pageSize;
StringBuilder sqlBuilder = new StringBuilder(sql.length() + 120);
sqlBuilder.append("SELECT * FROM (SELECT TMP_PAGE.*,ROWNUMBER() OVER() AS PAGEHELPER_ROW_ID FROM ( \n");
sqlBuilder.append(sql);
sqlBuilder.append("\n ) AS TMP_PAGE) TMP_PAGE WHERE PAGEHELPER_ROW_ID BETWEEN ");
sqlBuilder.append(startRow);
sqlBuilder.append(" AND ");
sqlBuilder.append(endRow);
return sqlBuilder.toString();
}
}

View File

@ -3,19 +3,26 @@ package ai.chat2db.plugin.dm;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import ai.chat2db.spi.MetaData;
import ai.chat2db.spi.jdbc.DefaultMetaService;
import ai.chat2db.spi.model.Function;
import ai.chat2db.spi.model.Procedure;
import ai.chat2db.spi.model.Table;
import ai.chat2db.spi.model.Trigger;
import ai.chat2db.spi.model.*;
import ai.chat2db.spi.sql.SQLExecutor;
import ai.chat2db.spi.util.SortUtils;
import ai.chat2db.spi.util.SqlUtils;
import jakarta.validation.constraints.NotEmpty;
public class DMMetaData extends DefaultMetaService implements MetaData {
private List<String> systemSchemas = Arrays.asList("CTISYS", "SYS","SYSDBA","SYSSSO","SYSAUDITOR");
@Override
public List<Schema> schemas(Connection connection, String databaseName) {
List<Schema> schemas = SQLExecutor.getInstance().schemas(connection, databaseName, null);
return SortUtils.sortSchema(schemas, systemSchemas);
}
public String tableDDL(Connection connection, String databaseName, String schemaName, String tableName) {
String selectObjectDDLSQL = String.format(
"select dbms_metadata.get_ddl(%s, %s, %s) AS \"sql\" from dual",

View File

@ -3,21 +3,24 @@ package ai.chat2db.plugin.h2;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import ai.chat2db.spi.MetaData;
import ai.chat2db.spi.jdbc.DefaultMetaService;
import ai.chat2db.spi.model.Function;
import ai.chat2db.spi.model.Procedure;
import ai.chat2db.spi.model.Table;
import ai.chat2db.spi.model.Trigger;
import ai.chat2db.spi.model.*;
import ai.chat2db.spi.sql.SQLExecutor;
import ai.chat2db.spi.util.SortUtils;
import jakarta.validation.constraints.NotEmpty;
public class H2Meta extends DefaultMetaService implements MetaData {
private List<String> systemSchemas = Arrays.asList("INFORMATION_SCHEMA");
@Override
public List<Schema> schemas(Connection connection, String databaseName) {
List<Schema> schemas = SQLExecutor.getInstance().schemas(connection, databaseName, null);
return SortUtils.sortSchema(schemas, systemSchemas);
}
@Override
public String tableDDL(Connection connection, @NotEmpty String databaseName, String schemaName,
@NotEmpty String tableName) {
@ -69,19 +72,13 @@ public class H2Meta extends DefaultMetaService implements MetaData {
createTableDDL.append(tableName).append(" (\n");
createTableDDL.append(String.join(",\n", columnDefinitions));
createTableDDL.append("\n);\n");
System.out.println("DDL建表语句");
System.out.println(createTableDDL.toString());
// 输出索引信息
System.out.println("\nDDL索引语句");
for (Map.Entry<String, List<String>> entry : indexMap.entrySet()) {
String indexName = entry.getKey();
List<String> columnList = entry.getValue();
String indexColumns = String.join(", ", columnList);
String createIndexDDL = String.format("CREATE INDEX %s ON %s (%s);", indexName, tableName,
indexColumns);
System.out.println(createIndexDDL);
createTableDDL.append(createIndexDDL);
}
return createTableDDL.toString();

View File

@ -5,6 +5,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import ai.chat2db.plugin.mysql.builder.MysqlSqlBuilder;
import ai.chat2db.plugin.mysql.type.MysqlCharsetEnum;
@ -17,9 +18,21 @@ import ai.chat2db.spi.jdbc.DefaultMetaService;
import ai.chat2db.spi.model.*;
import ai.chat2db.spi.sql.SQLExecutor;
import jakarta.validation.constraints.NotEmpty;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import static ai.chat2db.spi.util.SortUtils.sortDatabase;
public class MysqlMetaData extends DefaultMetaService implements MetaData {
private List<String> systemDatabases = Arrays.asList("information_schema", "performance_schema", "mysql", "sys");
@Override
public List<Database> databases(Connection connection) {
List<Database> databases = SQLExecutor.getInstance().databases(connection);
return sortDatabase(databases,systemDatabases,connection);
}
@Override
public String tableDDL(Connection connection, @NotEmpty String databaseName, String schemaName,
@NotEmpty String tableName) {

View File

@ -104,4 +104,22 @@ public class MysqlSqlBuilder implements SqlBuilder {
}
@Override
public String pageLimit(String sql, int offset, int pageNo, int pageSize) {
StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);
sqlBuilder.append(sql);
if (offset == 0) {
sqlBuilder.append("\n LIMIT ");
sqlBuilder.append(pageSize);
} else {
sqlBuilder.append("\n LIMIT ");
sqlBuilder.append(offset);
sqlBuilder.append(",");
sqlBuilder.append(pageSize);
}
return sqlBuilder.toString();
}
}

View File

@ -14,6 +14,7 @@ import ai.chat2db.spi.SqlBuilder;
import ai.chat2db.spi.jdbc.DefaultMetaService;
import ai.chat2db.spi.model.*;
import ai.chat2db.spi.sql.SQLExecutor;
import ai.chat2db.spi.util.SortUtils;
import com.google.common.collect.Lists;
import jakarta.validation.constraints.NotEmpty;
import org.apache.commons.lang3.StringUtils;
@ -22,6 +23,15 @@ public class OracleMetaData extends DefaultMetaService implements MetaData {
private static final String TABLE_DDL_SQL = "select dbms_metadata.get_ddl('TABLE','%s','%s') as sql from dual";
private List<String> systemSchemas = Arrays.asList("ANONYMOUS","APEX_030200","APEX_PUBLIC_USER","APPQOSSYS","BI","CTXSYS","DBSNMP","DIP","EXFSYS","FLOWS_FILES","HR","IX","MDDATA","MDSYS","MGMT_VIEW","OE","OLAPSYS","ORACLE_OCM","ORDDATA","ORDPLUGINS","ORDSYS","OUTLN","OWBSYS","OWBSYS_AUDIT","PM","SCOTT","SH","SI_INFORMTN_SCHEMA","SPATIAL_CSW_ADMIN_USR","SPATIAL_WFS_ADMIN_USR","SYS","SYSMAN","SYSTEM","WMSYS","XDB","XS$NULL");
@Override
public List<Schema> schemas(Connection connection, String databaseName) {
List<Schema> schemas = SQLExecutor.getInstance().schemas(connection, databaseName, null);
return SortUtils.sortSchema(schemas, systemSchemas);
}
@Override
public String tableDDL(Connection connection, String databaseName, String schemaName, String tableName) {
String sql = String.format(TABLE_DDL_SQL, tableName, schemaName);

View File

@ -6,6 +6,7 @@ import ai.chat2db.spi.SqlBuilder;
import ai.chat2db.spi.model.Table;
import ai.chat2db.spi.model.TableColumn;
import ai.chat2db.spi.model.TableIndex;
import ai.chat2db.spi.sql.Chat2DBContext;
import org.apache.commons.lang3.StringUtils;
public class OracleSqlBuilder implements SqlBuilder {
@ -99,4 +100,31 @@ public class OracleSqlBuilder implements SqlBuilder {
return script.toString();
}
@Override
public String pageLimit(String sql, int offset, int pageNo, int pageSize) {
int startRow = offset;
int endRow = offset + pageSize;
StringBuilder sqlBuilder = new StringBuilder(sql.length() + 120);
if (startRow > 0) {
sqlBuilder.append("SELECT * FROM ( ");
}
if (endRow > 0) {
sqlBuilder.append(" SELECT TMP_PAGE.*, ROWNUM CAHT2DB_AUTO_ROW_ID FROM ( ");
}
sqlBuilder.append("\n");
sqlBuilder.append(sql);
sqlBuilder.append("\n");
if (endRow > 0) {
sqlBuilder.append(" ) TMP_PAGE WHERE ROWNUM <= ");
sqlBuilder.append(endRow);
}
if (startRow > 0) {
sqlBuilder.append(" ) WHERE CAHT2DB_AUTO_ROW_ID > ");
sqlBuilder.append(startRow);
}
return sqlBuilder.toString();
}
}

View File

@ -17,22 +17,23 @@ import ai.chat2db.spi.SqlBuilder;
import ai.chat2db.spi.jdbc.DefaultMetaService;
import ai.chat2db.spi.model.*;
import ai.chat2db.spi.sql.SQLExecutor;
import com.alibaba.druid.sql.visitor.functions.If;
import com.alibaba.fastjson2.JSON;
import ai.chat2db.spi.util.SortUtils;
import com.google.common.collect.Lists;
import jakarta.validation.constraints.NotEmpty;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import static ai.chat2db.plugin.postgresql.consts.SQLConst.FUNCTION_SQL;
import static ai.chat2db.spi.util.SortUtils.sortDatabase;
public class PostgreSQLMetaData extends DefaultMetaService implements MetaData {
private static final String SELECT_KEY_INDEX = "SELECT ccu.table_schema AS Foreign_schema_name, ccu.table_name AS Foreign_table_name, ccu.column_name AS Foreign_column_name, constraint_type AS Constraint_type, tc.CONSTRAINT_NAME AS Key_name, tc.TABLE_NAME, kcu.Column_name, tc.is_deferrable, tc.initially_deferred FROM information_schema.table_constraints AS tc JOIN information_schema.key_column_usage AS kcu ON tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name WHERE tc.TABLE_SCHEMA = '%s' AND tc.TABLE_NAME = '%s';";
private List<String> systemDatabases = Arrays.asList("postgres");
@Override
public List<Database> databases(Connection connection) {
return SQLExecutor.getInstance().executeSql(connection, "SELECT datname FROM pg_database;", resultSet -> {
List<Database> list = SQLExecutor.getInstance().executeSql(connection, "SELECT datname FROM pg_database;", resultSet -> {
List<Database> databases = new ArrayList<>();
try {
while (resultSet.next()) {
@ -49,8 +50,30 @@ public class PostgreSQLMetaData extends DefaultMetaService implements MetaData {
}
return databases;
});
return sortDatabase(list, systemDatabases,connection);
}
private List<String> systemSchemas = Arrays.asList("pg_toast","pg_temp_1","pg_toast_temp_1","pg_catalog","information_schema");
@Override
public List<Schema> schemas(Connection connection, String databaseName) {
List<Schema> schemas = SQLExecutor.getInstance().execute(connection,
"SELECT catalog_name, schema_name FROM information_schema.schemata;", resultSet -> {
List<Schema> databases = new ArrayList<>();
while (resultSet.next()) {
Schema schema = new Schema();
String name = resultSet.getString("schema_name");
String catalogName = resultSet.getString("catalog_name");
schema.setName(name);
schema.setDatabaseName(catalogName);
databases.add(schema);
}
return databases;
});
return SortUtils.sortSchema(schemas, systemSchemas);
}
private static final String SELECT_TABLE_INDEX = "SELECT tmp.INDISPRIMARY AS Index_primary, tmp.TABLE_SCHEM, tmp.TABLE_NAME, tmp.NON_UNIQUE, tmp.INDEX_QUALIFIER, tmp.INDEX_NAME AS Key_name, tmp.indisclustered, tmp.ORDINAL_POSITION AS Seq_in_index, TRIM ( BOTH '\"' FROM pg_get_indexdef ( tmp.CI_OID, tmp.ORDINAL_POSITION, FALSE ) ) AS Column_name,CASE tmp.AM_NAME WHEN 'btree' THEN CASE tmp.I_INDOPTION [ tmp.ORDINAL_POSITION - 1 ] & 1 :: SMALLINT WHEN 1 THEN 'D' ELSE'A' END ELSE NULL END AS Collation, tmp.CARDINALITY, tmp.PAGES, tmp.FILTER_CONDITION , tmp.AM_NAME AS Index_method, tmp.DESCRIPTION AS Index_comment FROM ( SELECT n.nspname AS TABLE_SCHEM, ct.relname AS TABLE_NAME, NOT i.indisunique AS NON_UNIQUE, NULL AS INDEX_QUALIFIER, ci.relname AS INDEX_NAME,i.INDISPRIMARY , i.indisclustered , ( information_schema._pg_expandarray ( i.indkey ) ).n AS ORDINAL_POSITION, ci.reltuples AS CARDINALITY, ci.relpages AS PAGES, pg_get_expr ( i.indpred, i.indrelid ) AS FILTER_CONDITION, ci.OID AS CI_OID, i.indoption AS I_INDOPTION, am.amname AS AM_NAME , d.description FROM pg_class ct JOIN pg_namespace n ON ( ct.relnamespace = n.OID ) JOIN pg_index i ON ( ct.OID = i.indrelid ) JOIN pg_class ci ON ( ci.OID = i.indexrelid ) JOIN pg_am am ON ( ci.relam = am.OID ) left outer join pg_description d on i.indexrelid = d.objoid WHERE n.nspname = '%s' AND ct.relname = '%s' ) AS tmp ;";
private static String ROUTINES_SQL
= " SELECT p.proname, p.prokind, pg_catalog.pg_get_functiondef(p.oid) as \"code\" FROM pg_catalog.pg_proc p "
@ -99,22 +122,6 @@ public class PostgreSQLMetaData extends DefaultMetaService implements MetaData {
});
}
@Override
public List<Schema> schemas(Connection connection, String databaseName) {
return SQLExecutor.getInstance().execute(connection,
"SELECT catalog_name, schema_name FROM information_schema.schemata;", resultSet -> {
List<Schema> databases = new ArrayList<>();
while (resultSet.next()) {
Schema schema = new Schema();
String name = resultSet.getString("schema_name");
String catalogName = resultSet.getString("catalog_name");
schema.setName(name);
schema.setDatabaseName(catalogName);
databases.add(schema);
}
return databases;
});
}
@Override
public Function function(Connection connection, @NotEmpty String databaseName, String schemaName,

View File

@ -156,5 +156,20 @@ public class PostgreSQLSqlBuilder implements SqlBuilder {
return script.toString();
}
@Override
public String pageLimit(String sql, int offset, int pageNo, int pageSize) {
StringBuilder sqlStr = new StringBuilder(sql.length() + 17);
sqlStr.append(sql);
if (offset == 0) {
sqlStr.append(" LIMIT ");
sqlStr.append(pageSize);
} else {
sqlStr.append(" LIMIT ");
sqlStr.append(pageSize);
sqlStr.append(" OFFSET ");
sqlStr.append(offset);
}
return sqlStr.toString();
}
}

View File

@ -75,5 +75,8 @@ public class SqliteBuilder implements SqlBuilder {
return script.toString();
}
@Override
public String pageLimit(String sql, int offset, int pageNo, int pageSize) {
return "select * from(" + sql + ") t LIMIT " + pageNo + " OFFSET " + offset + "";
}
}

View File

@ -14,10 +14,33 @@ import ai.chat2db.spi.SqlBuilder;
import ai.chat2db.spi.jdbc.DefaultMetaService;
import ai.chat2db.spi.model.*;
import ai.chat2db.spi.sql.SQLExecutor;
import ai.chat2db.spi.util.SortUtils;
import jakarta.validation.constraints.NotEmpty;
import org.apache.commons.lang3.StringUtils;
import static ai.chat2db.spi.util.SortUtils.sortDatabase;
public class SqlServerMetaData extends DefaultMetaService implements MetaData {
private List<String> systemDatabases = Arrays.asList("master", "model", "msdb", "tempdb");
@Override
public List<Database> databases(Connection connection) {
List<Database> databases = SQLExecutor.getInstance().databases(connection);
return sortDatabase(databases,systemDatabases,connection);
}
private List<String> systemSchemas = Arrays.asList("guest", "INFORMATION_SCHEMA", "sys", "db_owner",
"db_accessadmin", "db_securityadmin", "db_ddladmin", "db_backupoperator", "db_datareader", "db_datawriter",
"db_denydatareader", "db_denydatawriter");
@Override
public List<Schema> schemas(Connection connection, String databaseName) {
List<Schema> schemas = SQLExecutor.getInstance().schemas(connection, databaseName, null);
return SortUtils.sortSchema(schemas, systemSchemas);
}
private String functionSQL
= "CREATE FUNCTION tableSchema.ufn_GetCreateTableScript( @schema_name NVARCHAR(128), @table_name NVARCHAR"
+ "(128)) RETURNS NVARCHAR(MAX) AS BEGIN DECLARE @CreateTableScript NVARCHAR(MAX); DECLARE @IndexScripts "
@ -49,7 +72,6 @@ public class SqlServerMetaData extends DefaultMetaService implements MetaData {
@Override
public String tableDDL(Connection connection, String databaseName, String schemaName, String tableName) {
try {
System.out.println(functionSQL);
SQLExecutor.getInstance().executeSql(connection, functionSQL.replace("tableSchema", schemaName),
resultSet -> null);
} catch (Exception e) {

View File

@ -6,6 +6,7 @@ import ai.chat2db.spi.SqlBuilder;
import ai.chat2db.spi.model.Table;
import ai.chat2db.spi.model.TableColumn;
import ai.chat2db.spi.model.TableIndex;
import ai.chat2db.spi.sql.Chat2DBContext;
import org.apache.commons.lang3.StringUtils;
public class SqlServerSqlBuilder implements SqlBuilder {
@ -110,8 +111,8 @@ public class SqlServerSqlBuilder implements SqlBuilder {
return script.toString();
}
private static String UPDATE_TABLE_COMMENT_SCRIPT = "exec sp_updateextendedproperty 'MS_Description','%s','SCHEMA','%s','TABLE','%s' \ngo";
private String buildUpdateTableComment(Table newTable) {
return String.format(UPDATE_TABLE_COMMENT_SCRIPT, newTable.getComment(), newTable.getSchemaName(), newTable.getName());
}
@ -121,4 +122,24 @@ public class SqlServerSqlBuilder implements SqlBuilder {
private String buildRenameTable(Table oldTable, Table newTable) {
return String.format(RENAME_TABLE_SCRIPT, oldTable.getName(), newTable.getName());
}
@Override
public String pageLimit(String sql, int offset, int pageNo, int pageSize) {
String version = Chat2DBContext.getDbVersion();
if (StringUtils.isNotBlank(version)) {
String[] versions = version.split("\\.");
if (versions.length > 0 && Integer.parseInt(versions[0]) >= 11) {
StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);
sqlBuilder.append(sql);
sqlBuilder.append("\n OFFSET ");
sqlBuilder.append(offset);
sqlBuilder.append(" ROWS ");
sqlBuilder.append(" FETCH NEXT ");
sqlBuilder.append(pageSize);
sqlBuilder.append(" ROWS ONLY");
return sqlBuilder.toString();
}
}
return "";
}
}

View File

@ -51,33 +51,7 @@ public class DatabaseServiceImpl implements DatabaseService {
}
private List<Database> getDatabases(String dbType, Connection connection) {
MetaData metaData = Chat2DBContext.getMetaData(dbType);
List<Database> databases = metaData.databases(connection);
sortDatabases(databases,connection);
return databases;
}
private void sortDatabases(List<Database> databases,Connection connection) {
if (CollectionUtils.isEmpty(databases)) {
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 < databases.size(); i++) {
if (StringUtils.isNotBlank(ulr) && StringUtils.isNotBlank(databases.get(i).getName())&& ulr.contains(databases.get(i).getName())) {
num = i;
break;
}
}
if (num != -1 && num != 0) {
Collections.swap(databases, num, 0);
}
return Chat2DBContext.getMetaData(dbType).databases(connection);
}
@Override

View File

@ -146,14 +146,24 @@ public class DlTemplateServiceImpl implements DlTemplateService {
} catch (ParserException e) {
log.warn("解析sql失败:{}", originalSql, e);
}
ExecuteResult executeResult = null;
if (SqlTypeEnum.SELECT.getCode().equals(sqlType) && !SqlUtils.hasPageLimit(originalSql,dbType)) {
String pageLimit = Chat2DBContext.getSqlBuilder().pageLimit(originalSql, offset, pageNo, pageSize);
if(StringUtils.isNotBlank(pageLimit)) {
executeResult = execute(pageLimit, 0, count);
}
}
if (executeResult == null || !executeResult.getSuccess()) {
executeResult = execute(originalSql, offset, count);
}
ExecuteResult 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);
}
if (SqlTypeEnum.SELECT.getCode().equals(sqlType)) {
executeResult.setPageNo(pageNo);

View File

@ -1,7 +1,10 @@
package ai.chat2db.server.domain.repository.mapper;
import ai.chat2db.server.domain.repository.entity.TableCacheDO;
import ai.chat2db.server.domain.repository.entity.TeamUserDO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@ -16,4 +19,6 @@ import java.util.List;
public interface TableCacheMapper extends BaseMapper<TableCacheDO> {
void batchInsert(List<TableCacheDO> list);
IPage<TableCacheDO> pageQuery(IPage<TableCacheDO> page, @Param("dataSourceId") Long dataSourceId, @Param("databaseName") String databaseName, @Param("schemaName") String schemaName, @Param("searchKey") String searchKey);
}

View File

@ -10,4 +10,23 @@
(#{item.dataSourceId},#{item.databaseName},#{item.schemaName},#{item.tableName},#{item.key},#{item.version},#{item.columns},#{item.extendInfo})
</foreach>
</insert>
<select id="pageQuery" resultType="ai.chat2db.server.domain.repository.entity.TableCacheDO">
select *
from TABLE_CACHE tc
<where>
<if test="dataSourceId != null ">
and tc.data_source_id = #{dataSourceId}
</if>
<if test="databaseName != null and databaseName != '' ">
and tc.database_name = #{databaseName}
</if>
<if test="schemaName != null and schemaName != '' ">
and tc.schema_name = #{schemaName}
</if>
<if test="searchKey != null and searchKey != '' ">
and LOWER(tc.table_name) like LOWER(concat('%',#{searchKey},'%'))
</if>
</where>
</select>
</mapper>

View File

@ -35,6 +35,17 @@
<version>2.0.4</version>
<scope>test</scope>
</dependency>
<!-- <dependency>-->
<!-- <groupId>mysql</groupId>-->
<!-- <artifactId>mysql-connector-java</artifactId>-->
<!-- <version>8.0.30</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>com.github.jsqlparser</groupId>-->
<!-- <artifactId>jsqlparser</artifactId>-->
<!-- <version>4.6</version>-->
<!-- <scope>test</scope>-->
<!-- </dependency>-->
</dependencies>
</project>

View File

@ -1,21 +1,42 @@
package ai.chat2db.server.web.api.controller.system;
import ai.chat2db.server.tools.common.util.ConfigUtils;
import ai.chat2db.server.web.api.controller.system.util.SystemUtils;
import ai.chat2db.server.web.api.controller.system.vo.AppVersionVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class AutomaticUpgrade {
@Scheduled(fixedRate = 3600000) // 每小时运行一次
public void checkVersionUpdates() {
AppVersionVO appVersion = SystemUtils.getLatestVersion(ConfigUtils.getLocalVersion(), "auto", "");
if (appVersion != null) {
SystemUtils.upgrade(appVersion);
}
}
}
////package ai.chat2db.server.web.api.controller.system;
////
////import ai.chat2db.server.tools.common.util.ConfigUtils;
////import ai.chat2db.server.web.api.controller.system.util.SystemUtils;
////import ai.chat2db.server.web.api.controller.system.vo.AppVersionVO;
////import lombok.extern.slf4j.Slf4j;
////import org.springframework.scheduling.annotation.Scheduled;
////import org.springframework.stereotype.Component;
////
////@Slf4j
////@Component
////public class AutomaticUpgrade {
////
//// @Scheduled(fixedRate = 3600000) // 每小时运行一次
//// public void checkVersionUpdates() {
//// AppVersionVO appVersion = SystemUtils.getLatestVersion(ConfigUtils.getLocalVersion(), "auto", "");
//// if (appVersion != null) {
//// SystemUtils.upgrade(appVersion);
//// }
//// }
////}
//
//const handleInsertText = (text, databaseCode: DatabaseTypeCode = DatabaseTypeCode.MYSQL) => {
// if (
// [DatabaseTypeCode.POSTGRESQL, DatabaseTypeCode.ORACLE, DatabaseTypeCode.DB2, DatabaseTypeCode.SQLITE].includes(
// databaseCode,
// )
// ) {
// return `\"${text}\"`;
// } else if ([DatabaseTypeCode.SQLSERVER].includes(databaseCode)) {
// return `[${text}]`;
// } else if ([DatabaseTypeCode.MYSQL].includes(databaseCode)) {
// return `\`${text}\``;
// } else {
// return `${text}`;
// }
// };
//
//"" oracele sqlite postgrsql h2 dm
// ` MYSQL clickhouse MariaDB
// [ sqlserver
//

View File

@ -206,5 +206,4 @@ public interface MetaData {
*/
String getMetaDataName(String ...names);
}

View File

@ -21,4 +21,16 @@ public interface SqlBuilder {
* @return
*/
String buildModifyTaleSql(Table oldTable, Table newTable);
/**
* Generate page limit sql
*
* @param sql
* @param offset
* @param pageNo
* @param pageSize
* @return
*/
String pageLimit(String sql, int offset, int pageNo, int pageSize);
}

View File

@ -14,4 +14,9 @@ public class DefaultSqlBuilder implements SqlBuilder {
public String buildModifyTaleSql(Table oldTable, Table newTable) {
return null;
}
@Override
public String pageLimit(String sql, int offset, int pageNo, int pageSize) {
return null;
}
}

View File

@ -91,6 +91,26 @@ public class Chat2DBContext {
return connection;
}
public static String getDbVersion() {
ConnectInfo connectInfo = getConnectInfo();
String dbVersion = connectInfo.getDbVersion();
if (dbVersion == null) {
synchronized (connectInfo) {
if (connectInfo.getDbVersion() != null) {
return connectInfo.getDbVersion();
} else {
dbVersion = SQLExecutor.getInstance().getDbVersion(getConnection());
connectInfo.setDbVersion(dbVersion);
return connectInfo.getDbVersion();
}
}
} else {
return dbVersion;
}
}
/**
* 设置context
*

View File

@ -129,12 +129,23 @@ public class ConnectInfo {
public Connection connection;
/**
* Database version used for different database
*/
private String dbVersion;
private DriverConfig driverConfig;
public String getDbVersion() {
return dbVersion;
}
public void setDbVersion(String dbVersion) {
this.dbVersion = dbVersion;
}
public DriverConfig getDriverConfig() {
return driverConfig;
}

View File

@ -17,6 +17,7 @@ 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;
@ -185,6 +186,11 @@ public class SQLExecutor {
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 (offset != null && count != null) {
stmt.setMaxRows(offset + count);
}
TimeInterval timeInterval = new TimeInterval();
boolean query = stmt.execute(sql);
executeResult.setDescription(I18nUtils.getMessage("sqlResult.success"));
@ -200,11 +206,19 @@ public class SQLExecutor {
// 获取header信息
List<Header> headerList = Lists.newArrayListWithExpectedSize(col);
executeResult.setHeaderList(headerList);
int chat2dbAutoRowIdIndex = -1;// chat2db自动生成的行分页ID
for (int i = 1; i <= col; i++) {
String name = ResultSetUtils.getColumnName(resultSetMetaData, i);
if ("CAHT2DB_AUTO_ROW_ID".equals(name)) {
chat2dbAutoRowIdIndex = i;
continue;
}
String dataType = ai.chat2db.spi.util.JdbcUtils.resolveDataType(
resultSetMetaData.getColumnTypeName(i), resultSetMetaData.getColumnType(i)).getCode();
headerList.add(Header.builder()
.dataType(ai.chat2db.spi.util.JdbcUtils.resolveDataType(
resultSetMetaData.getColumnTypeName(i), resultSetMetaData.getColumnType(i)).getCode())
.name(ResultSetUtils.getColumnName(resultSetMetaData, i))
.dataType(dataType)
.name(name)
.build());
}
@ -224,6 +238,9 @@ public class SQLExecutor {
List<String> row = Lists.newArrayListWithExpectedSize(col);
dataList.add(row);
for (int i = 1; i <= col; i++) {
if (chat2dbAutoRowIdIndex == i) {
continue;
}
row.add(ai.chat2db.spi.util.JdbcUtils.getResultSetValue(rs, i, limitRowSize));
}
if (count != null && count > 0 && rowCount++ >= count) {
@ -263,7 +280,11 @@ public class SQLExecutor {
*/
public List<Database> databases(Connection connection) {
try (ResultSet resultSet = connection.getMetaData().getCatalogs();) {
return ResultSetUtils.toObjectList(resultSet, Database.class);
List<Database> databases = ResultSetUtils.toObjectList(resultSet, Database.class);
if (CollectionUtils.isEmpty(databases)) {
return databases;
}
return databases.stream().filter(database -> database.getName() != null).collect(Collectors.toList());
} catch (SQLException e) {
throw new RuntimeException(e);
}
@ -428,4 +449,15 @@ public class SQLExecutor {
}
}
public String getDbVersion(Connection connection) {
try {
String dbVersion = connection.getMetaData().getDatabaseProductVersion();
return dbVersion;
} catch (Exception e) {
log.error("get db version error", e);
//throw new RuntimeException(e);
}
return "";
}
}

View File

@ -42,7 +42,6 @@ public class JdbcJarUtils {
String outputPath = PATH + url.substring(url.lastIndexOf("/") + 1);
File file = new File(outputPath);
if (file.exists()) {
System.out.println("File already exists: " + outputPath);
continue;
}
asyncDownload(url);
@ -77,7 +76,6 @@ public class JdbcJarUtils {
}
fos.flush();
}
System.out.println("File downloaded: " + outputPath);
}
});
}
@ -105,7 +103,6 @@ public class JdbcJarUtils {
}
fos.flush();
}
System.out.println("File downloaded: " + outputPath);
}
}

View File

@ -0,0 +1,77 @@
package ai.chat2db.spi.util;
import ai.chat2db.spi.model.Database;
import ai.chat2db.spi.model.Schema;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class SortUtils {
public static List<Database> sortDatabase(List<Database> databases, List<String> list, Connection connection) {
if (CollectionUtils.isEmpty(databases)) {
return databases;
}
List<Database> systemDatabases = databases.stream()
.filter(database -> list.contains(database.getName())).collect(Collectors.toList());
List<Database> userDatabases = databases.stream()
.filter(database -> !list.contains(database.getName())).collect(Collectors.toList());
if (CollectionUtils.isEmpty(userDatabases)) {
return databases;
}
if (CollectionUtils.isEmpty(systemDatabases)) {
return userDatabases;
}
List<Database> databaseList = Stream.concat(userDatabases.stream(), systemDatabases.stream())
.collect(Collectors.toList());
// If the database name contains the name of the current database, the current database is placed in the first place
String ulr;
try {
ulr = connection.getMetaData().getURL();
} catch (SQLException e) {
return databaseList;
}
// If the database name contains the name of the current database, the current database is placed in the first place
int no = -1;
for (int i = 0; i < databases.size(); i++) {
if (StringUtils.isNotBlank(ulr)
&& StringUtils.isNotBlank(databases.get(i).getName())
&& ulr.contains(databases.get(i).getName())
&& !"mysql".equalsIgnoreCase(databases.get(i).getName())) {
no = i;
break;
}
}
if (no != -1 && no != 0) {
Collections.swap(databaseList, no, 0);
}
return databaseList;
}
public static List<Schema> sortSchema(List<Schema> schemas, List<String> systemSchemas) {
if (CollectionUtils.isEmpty(schemas)) {
return schemas;
}
List<Schema> systemSchema = schemas.stream()
.filter(schema -> systemSchemas.contains(schema.getName()) || "APEX_".startsWith(schema.getName())).collect(Collectors.toList());
List<Schema> userSchema = schemas.stream()
.filter(schema -> !systemSchemas.contains(schema.getName()) && !"APEX_".startsWith(schema.getName())).collect(Collectors.toList());
if (CollectionUtils.isEmpty(userSchema)) {
return schemas;
}
if (CollectionUtils.isEmpty(systemSchema)) {
return userSchema;
}
return Stream.concat(userSchema.stream(), systemSchema.stream())
.collect(Collectors.toList());
}
}

View File

@ -12,13 +12,12 @@ import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource;
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
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.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.Statements;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectExpressionItem;
import net.sf.jsqlparser.statement.select.SelectItem;
import net.sf.jsqlparser.statement.select.*;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
@ -49,6 +48,17 @@ public class SqlUtils {
executeResult.setCanEdit(false);
return;
}
if (item instanceof SelectExpressionItem) {
SelectExpressionItem selectExpressionItem = (SelectExpressionItem) item;
// 如果表达式是一个函数
if (selectExpressionItem.getExpression() instanceof Function) {
Function function = (Function) selectExpressionItem.getExpression();
// 检查函数是否为 "COUNT"
if ("COUNT".equalsIgnoreCase(function.getName())) {
executeResult.setCanEdit(false);
return; }
}
}
}
}
executeResult.setCanEdit(true);
@ -58,6 +68,8 @@ public class SqlUtils {
sqlSelectStatement.getSelect().getFirstQueryBlock().getFrom());
executeResult.setTableName(getMetaDataTableName(sqlExprTableSource.getCatalog(), sqlExprTableSource.getSchema(), sqlExprTableSource.getTableName()));
}
}else {
executeResult.setCanEdit(false);
}
}
} catch (Exception e) {
@ -116,4 +128,29 @@ public class SqlUtils {
DataTypeEnum dataTypeEnum = DataTypeEnum.getByCode(dataType);
return dataTypeEnum.getSqlValue(value);
}
public static boolean hasPageLimit(String sql, DbType dbType) {
try {
Statement statement = CCJSqlParserUtil.parse(sql);
if (statement instanceof Select) {
Select selectStatement = (Select) statement;
SelectBody selectBody = selectStatement.getSelectBody();
// 检查常见的分页方法
if (selectBody instanceof PlainSelect) {
PlainSelect plainSelect = (PlainSelect) selectBody;
// 检查 LIMIT
if (plainSelect.getLimit() != null || plainSelect.getOffset() != null || plainSelect.getTop() != null || plainSelect.getFetch() != null) {
return true;
}
if (DbType.oracle.equals(dbType)) {
return sql.contains("ROWNUM") || sql.contains("rownum");
}
}
}
} catch (Exception e) {
return false;
}
return false;
}
}