support sqlserver metadata update

This commit is contained in:
SwallowGG
2023-10-07 19:27:06 +08:00
parent d483dd1df2
commit 04462d7811
20 changed files with 1002 additions and 205 deletions

View File

@ -15,6 +15,11 @@
<groupId>ai.chat2db</groupId>
<artifactId>chat2db-spi</artifactId>
</dependency>
<dependency>
<groupId>ai.chat2db</groupId>
<artifactId>chat2db-mysql</artifactId>
<version>2.0.0-SNAPSHOT</version>
</dependency>
</dependencies>
<artifactId>chat2db-mariadb</artifactId>

View File

@ -4,6 +4,7 @@ import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
import ai.chat2db.plugin.mysql.MysqlMetaData;
import ai.chat2db.spi.MetaData;
import ai.chat2db.spi.jdbc.DefaultMetaService;
import ai.chat2db.spi.model.Function;
@ -13,109 +14,109 @@ import ai.chat2db.spi.model.Trigger;
import ai.chat2db.spi.sql.SQLExecutor;
import jakarta.validation.constraints.NotEmpty;
public class MariaDBMetaData extends DefaultMetaService implements MetaData {
public class MariaDBMetaData extends MysqlMetaData implements MetaData {
private static String ROUTINES_SQL
=
"SELECT SPECIFIC_NAME, ROUTINE_COMMENT, ROUTINE_DEFINITION FROM information_schema.routines WHERE "
+ "routine_type = '%s' AND ROUTINE_SCHEMA ='%s' AND "
+ "routine_name = '%s';";
@Override
public Function function(Connection connection, @NotEmpty String databaseName, String schemaName,
String functionName) {
String sql = String.format(ROUTINES_SQL, "FUNCTION", databaseName, functionName);
return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
Function function = new Function();
function.setDatabaseName(databaseName);
function.setSchemaName(schemaName);
function.setFunctionName(functionName);
if (resultSet.next()) {
function.setSpecificName(resultSet.getString("SPECIFIC_NAME"));
function.setRemarks(resultSet.getString("ROUTINE_COMMENT"));
function.setFunctionBody(resultSet.getString("ROUTINE_DEFINITION"));
}
return function;
});
}
private static String TRIGGER_SQL
= "SELECT TRIGGER_NAME,EVENT_MANIPULATION, ACTION_STATEMENT FROM INFORMATION_SCHEMA.TRIGGERS where "
+ "TRIGGER_SCHEMA = '%s' AND TRIGGER_NAME = '%s';";
private static String TRIGGER_SQL_LIST
= "SELECT TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS where TRIGGER_SCHEMA = '%s';";
@Override
public List<Trigger> triggers(Connection connection, String databaseName, String schemaName) {
List<Trigger> triggers = new ArrayList<>();
String sql = String.format(TRIGGER_SQL_LIST, databaseName);
return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
while (resultSet.next()) {
Trigger trigger = new Trigger();
trigger.setTriggerName(resultSet.getString("TRIGGER_NAME"));
trigger.setSchemaName(schemaName);
trigger.setDatabaseName(databaseName);
triggers.add(trigger);
}
return triggers;
});
}
@Override
public Trigger trigger(Connection connection, @NotEmpty String databaseName, String schemaName,
String triggerName) {
String sql = String.format(TRIGGER_SQL, databaseName, triggerName);
return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
Trigger trigger = new Trigger();
trigger.setDatabaseName(databaseName);
trigger.setSchemaName(schemaName);
trigger.setTriggerName(triggerName);
if (resultSet.next()) {
trigger.setTriggerBody(resultSet.getString("ACTION_STATEMENT"));
}
return trigger;
});
}
@Override
public Procedure procedure(Connection connection, @NotEmpty String databaseName, String schemaName,
String procedureName) {
String sql = String.format(ROUTINES_SQL, "PROCEDURE", databaseName, procedureName);
return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
Procedure procedure = new Procedure();
procedure.setDatabaseName(databaseName);
procedure.setSchemaName(schemaName);
procedure.setProcedureName(procedureName);
if (resultSet.next()) {
procedure.setSpecificName(resultSet.getString("SPECIFIC_NAME"));
procedure.setRemarks(resultSet.getString("ROUTINE_COMMENT"));
procedure.setProcedureBody(resultSet.getString("ROUTINE_DEFINITION"));
}
return procedure;
});
}
private static String VIEW_SQL
= "SELECT TABLE_SCHEMA AS DatabaseName, TABLE_NAME AS ViewName, VIEW_DEFINITION AS definition, CHECK_OPTION, "
+ "IS_UPDATABLE FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s';";
@Override
public Table view(Connection connection, String databaseName, String schemaName, String viewName) {
String sql = String.format(VIEW_SQL, databaseName, viewName);
return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
Table table = new Table();
table.setDatabaseName(databaseName);
table.setSchemaName(schemaName);
table.setName(viewName);
if (resultSet.next()) {
table.setDdl(resultSet.getString("definition"));
}
return table;
});
}
// private static String ROUTINES_SQL
// =
// "SELECT SPECIFIC_NAME, ROUTINE_COMMENT, ROUTINE_DEFINITION FROM information_schema.routines WHERE "
// + "routine_type = '%s' AND ROUTINE_SCHEMA ='%s' AND "
// + "routine_name = '%s';";
//
// @Override
// public Function function(Connection connection, @NotEmpty String databaseName, String schemaName,
// String functionName) {
//
// String sql = String.format(ROUTINES_SQL, "FUNCTION", databaseName, functionName);
// return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
// Function function = new Function();
// function.setDatabaseName(databaseName);
// function.setSchemaName(schemaName);
// function.setFunctionName(functionName);
// if (resultSet.next()) {
// function.setSpecificName(resultSet.getString("SPECIFIC_NAME"));
// function.setRemarks(resultSet.getString("ROUTINE_COMMENT"));
// function.setFunctionBody(resultSet.getString("ROUTINE_DEFINITION"));
// }
// return function;
// });
//
// }
//
// private static String TRIGGER_SQL
// = "SELECT TRIGGER_NAME,EVENT_MANIPULATION, ACTION_STATEMENT FROM INFORMATION_SCHEMA.TRIGGERS where "
// + "TRIGGER_SCHEMA = '%s' AND TRIGGER_NAME = '%s';";
//
// private static String TRIGGER_SQL_LIST
// = "SELECT TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS where TRIGGER_SCHEMA = '%s';";
//
// @Override
// public List<Trigger> triggers(Connection connection, String databaseName, String schemaName) {
// List<Trigger> triggers = new ArrayList<>();
// String sql = String.format(TRIGGER_SQL_LIST, databaseName);
// return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
// while (resultSet.next()) {
// Trigger trigger = new Trigger();
// trigger.setTriggerName(resultSet.getString("TRIGGER_NAME"));
// trigger.setSchemaName(schemaName);
// trigger.setDatabaseName(databaseName);
// triggers.add(trigger);
// }
// return triggers;
// });
// }
//
// @Override
// public Trigger trigger(Connection connection, @NotEmpty String databaseName, String schemaName,
// String triggerName) {
//
// String sql = String.format(TRIGGER_SQL, databaseName, triggerName);
// return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
// Trigger trigger = new Trigger();
// trigger.setDatabaseName(databaseName);
// trigger.setSchemaName(schemaName);
// trigger.setTriggerName(triggerName);
// if (resultSet.next()) {
// trigger.setTriggerBody(resultSet.getString("ACTION_STATEMENT"));
// }
// return trigger;
// });
// }
//
// @Override
// public Procedure procedure(Connection connection, @NotEmpty String databaseName, String schemaName,
// String procedureName) {
// String sql = String.format(ROUTINES_SQL, "PROCEDURE", databaseName, procedureName);
// return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
// Procedure procedure = new Procedure();
// procedure.setDatabaseName(databaseName);
// procedure.setSchemaName(schemaName);
// procedure.setProcedureName(procedureName);
// if (resultSet.next()) {
// procedure.setSpecificName(resultSet.getString("SPECIFIC_NAME"));
// procedure.setRemarks(resultSet.getString("ROUTINE_COMMENT"));
// procedure.setProcedureBody(resultSet.getString("ROUTINE_DEFINITION"));
// }
// return procedure;
// });
// }
//
// private static String VIEW_SQL
// = "SELECT TABLE_SCHEMA AS DatabaseName, TABLE_NAME AS ViewName, VIEW_DEFINITION AS definition, CHECK_OPTION, "
// + "IS_UPDATABLE FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s';";
//
// @Override
// public Table view(Connection connection, String databaseName, String schemaName, String viewName) {
// String sql = String.format(VIEW_SQL, databaseName, viewName);
// return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
// Table table = new Table();
// table.setDatabaseName(databaseName);
// table.setSchemaName(schemaName);
// table.setName(viewName);
// if (resultSet.next()) {
// table.setDdl(resultSet.getString("definition"));
// }
// return table;
// });
// }
}

View File

@ -178,7 +178,6 @@ public class MysqlMetaData extends DefaultMetaService implements MetaData {
}
private static String VIEW_SQL
= "SELECT TABLE_SCHEMA AS DatabaseName, TABLE_NAME AS ViewName, VIEW_DEFINITION AS definition, CHECK_OPTION, "
+ "IS_UPDATABLE FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s';";
@ -271,4 +270,9 @@ public class MysqlMetaData extends DefaultMetaService implements MetaData {
.collations(MysqlCollationEnum.getCollations())
.build();
}
@Override
public String getMetaDataName(String... names) {
return Arrays.stream(names).map(name -> "`" + name + "`").collect(Collectors.joining("."));
}
}

View File

@ -281,4 +281,9 @@ public class OracleMetaData extends DefaultMetaService implements MetaData {
.collations(Lists.newArrayList())
.build();
}
@Override
public String getMetaDataName(String... names) {
return Arrays.stream(names).map(name -> "\"" + name + "\"").collect(Collectors.joining("."));
}
}

View File

@ -79,6 +79,9 @@ public class OracleSqlBuilder implements SqlBuilder {
if (StringUtils.isNotBlank(tableColumn.getEditStatus())) {
OracleColumnTypeEnum typeEnum = OracleColumnTypeEnum.getByType(tableColumn.getColumnType());
script.append("\t").append(typeEnum.buildModifyColumn(tableColumn)).append(";\n");
if(StringUtils.isNotBlank(tableColumn.getComment())){
script.append("\n").append(buildComment(tableColumn)).append(";\n");
}
}
}

View File

@ -2,7 +2,9 @@ package ai.chat2db.plugin.sqlite;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import ai.chat2db.spi.MetaData;
import ai.chat2db.spi.jdbc.DefaultMetaService;
@ -35,4 +37,9 @@ public class SqliteMetaData extends DefaultMetaService implements MetaData {
public List<Schema> schemas(Connection connection,String databaseName) {
return Lists.newArrayList();
}
@Override
public String getMetaDataName(String... names) {
return Arrays.stream(names).collect(Collectors.joining("."));
}
}

View File

@ -17,6 +17,12 @@
<groupId>ai.chat2db</groupId>
<artifactId>chat2db-spi</artifactId>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>11.2.1.jre17</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,124 @@
package ai.chat2db.plugin.sqlserver.builder;
import ai.chat2db.plugin.sqlserver.type.SqlServerColumnTypeEnum;
import ai.chat2db.plugin.sqlserver.type.SqlServerIndexTypeEnum;
import ai.chat2db.spi.SqlBuilder;
import ai.chat2db.spi.model.Table;
import ai.chat2db.spi.model.TableColumn;
import ai.chat2db.spi.model.TableIndex;
import org.apache.commons.lang3.StringUtils;
public class SqlServerSqlBuilder implements SqlBuilder {
@Override
public String buildCreateTableSql(Table table) {
StringBuilder script = new StringBuilder();
script.append("CREATE TABLE ").append("[").append(table.getSchemaName()).append("].[").append(table.getName()).append("] (").append("\n");
for (TableColumn column : table.getColumnList()) {
if (StringUtils.isBlank(column.getName()) || StringUtils.isBlank(column.getColumnType())) {
continue;
}
SqlServerColumnTypeEnum typeEnum = SqlServerColumnTypeEnum.getByType(column.getColumnType());
script.append("\t").append(typeEnum.buildCreateColumnSql(column)).append(",\n");
}
script = new StringBuilder(script.substring(0, script.length() - 2));
script.append("\n)\ngo\n");
for (TableIndex tableIndex : table.getIndexList()) {
if (StringUtils.isBlank(tableIndex.getName()) || StringUtils.isBlank(tableIndex.getType())) {
continue;
}
SqlServerIndexTypeEnum sqlServerIndexTypeEnum = SqlServerIndexTypeEnum.getByType(tableIndex.getType());
script.append("\n").append(sqlServerIndexTypeEnum.buildIndexScript(tableIndex));
if (StringUtils.isNotBlank(tableIndex.getComment())) {
script.append("\n").append(buildIndexComment(tableIndex));
}
}
for (TableColumn column : table.getColumnList()) {
if (StringUtils.isBlank(column.getName()) || StringUtils.isBlank(column.getColumnType()) || StringUtils.isBlank(column.getComment())) {
continue;
}
script.append("\n").append(buildColumnComment(column));
}
if (StringUtils.isNotBlank(table.getComment())) {
script.append("\n").append(buildTableComment(table));
}
return script.toString();
}
private static String INDEX_COMMENT_SCRIPT = "exec sp_addextendedproperty 'MS_Description','%s','SCHEMA','%s','TABLE','%s','INDEX','%s' \ngo";
private String buildIndexComment(TableIndex tableIndex) {
return String.format(INDEX_COMMENT_SCRIPT, tableIndex.getComment(), tableIndex.getSchemaName(), tableIndex.getTableName(), tableIndex.getName());
}
private static String TABLE_COMMENT_SCRIPT = "exec sp_addextendedproperty 'MS_Description','%s','SCHEMA','%s','TABLE','%s' \ngo";
private String buildTableComment(Table table) {
return String.format(TABLE_COMMENT_SCRIPT, table.getComment(), table.getSchemaName(), table.getName());
}
private static String COLUMN_COMMENT_SCRIPT = "exec sp_addextendedproperty 'MS_Description','%s','SCHEMA','%s','TABLE','%s','COLUMN','%s' \ngo";
private String buildColumnComment(TableColumn column) {
return String.format(COLUMN_COMMENT_SCRIPT, column.getComment(), column.getSchemaName(), column.getTableName(), column.getName());
}
@Override
public String buildModifyTaleSql(Table oldTable, Table newTable) {
StringBuilder script = new StringBuilder();
if (!StringUtils.equalsIgnoreCase(oldTable.getName(), newTable.getName())) {
script.append(buildRenameTable(oldTable, newTable));
}
if (!StringUtils.equalsIgnoreCase(oldTable.getComment(), newTable.getComment())) {
if(oldTable.getComment() == null){
script.append("\n").append(buildTableComment(newTable));
}else {
script.append("\n").append(buildUpdateTableComment(newTable));
}
}
// append modify column
for (TableColumn tableColumn : newTable.getColumnList()) {
if (StringUtils.isNotBlank(tableColumn.getEditStatus())) {
SqlServerColumnTypeEnum typeEnum = SqlServerColumnTypeEnum.getByType(tableColumn.getColumnType());
script.append(typeEnum.buildModifyColumn(tableColumn)).append("\n");
}
}
// append modify index
for (TableIndex tableIndex : newTable.getIndexList()) {
if (StringUtils.isNotBlank(tableIndex.getEditStatus()) && StringUtils.isNotBlank(tableIndex.getType())) {
SqlServerIndexTypeEnum mysqlIndexTypeEnum = SqlServerIndexTypeEnum.getByType(tableIndex.getType());
script.append("\t").append(mysqlIndexTypeEnum.buildModifyIndex(tableIndex)).append("\n");
if (StringUtils.isNotBlank(tableIndex.getComment())) {
script.append("\n").append(buildIndexComment(tableIndex)).append("\ngo");
}
}
}
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());
}
private static String RENAME_TABLE_SCRIPT = "exec sp_rename '%s','%s','OBJECT' \ngo";
private String buildRenameTable(Table oldTable, Table newTable) {
return String.format(RENAME_TABLE_SCRIPT, oldTable.getName(), newTable.getName());
}
}

View File

@ -0,0 +1,337 @@
package ai.chat2db.plugin.sqlserver.type;
import ai.chat2db.spi.ColumnBuilder;
import ai.chat2db.spi.enums.EditStatus;
import ai.chat2db.spi.model.ColumnType;
import ai.chat2db.spi.model.TableColumn;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public enum SqlServerColumnTypeEnum implements ColumnBuilder {
//JSON("JSON", false, false, true, false, false, false, true, false, false, false)
BIGINT("BIGINT", false, false, true, false, false, false, true, true),
BINARY("BINARY", false, false, true, false, false, false, true, true),
BIT("BIT", false, false, true, false, false, false, true, true),
CHAR("CHAR", true, false, true, false, false, true, true, true),
DATE("DATE", false, false, true, false, false, false, true, true),
DATETIME("DATETIME", false, false, true, false, false, false, true, true),
DATETIME2("DATETIME2", true, false, true, false, false, false, true, true),
DATETIMEOFFSET("DATETIMEOFFSET", true, false, true, false, false, false, true, true),
DECIMAL("DECIMAL", true, true, true, false, false, false, true, true),
FLOAT("FLOAT", true, false, true, false, false, false, true, true),
GEOGRAPHY("GEOGRAPHY", false, false, true, false, false, false, true, true),
GEOMETRY("GEOMETRY", false, false, true, false, false, false, true, true),
HIERARCHYID("HIERARCHYID", false, false, true, false, false, false, true, true),
IMAGE("IMAGE", false, false, true, false, false, false, true, true),
INT("INT", false, false, true, false, false, false, true, true),
MONEY("MONEY", false, false, true, false, false, false, true, true),
NCHAR("NCHAR", true, false, true, false, false, true, true, true),
NTEXT("NTEXT", false, false, true, false, false, false, true, true),
NUMERIC("NUMERIC", true, true, true, false, false, false, true, true),
NVARCHAR("NVARCHAR", true, false, true, false, false, true, true, true),
NVARCHAR_MAX("NVARCHAR(MAX)", false, false, true, false, false, true, true, true),
REAL("REAL", false, false, true, false, false, false, true, true),
SMALLDATETIME("SMALLDATETIME", false, false, true, false, false, false, true, true),
SMALLINT("SMALLINT", false, false, true, false, false, false, true, true),
SMALLMONEY("SMALLMONEY", false, false, true, false, false, false, true, true),
SQL_VARIANT("SQL_VARIANT", false, false, true, false, false, false, true, true),
SYSNAME("SYSNAME", false, false, true, false, false, false, true, true),
TEXT("TEXT", false, false, true, false, false, true, true, true),
TIME("TIME", true, false, true, false, false, false, true, true),
TIMESTAMP("TIMESTAMP", false, false, true, false, false, false, true, true),
TINYINT("TINYINT", false, false, true, false, false, false, true, true),
UNIQUEIDENTIFIER("UNIQUEIDENTIFIER", false, false, true, false, false, false, true, true),
VARBINARY("VARBINARY", true, false, true, false, false, false, true, true),
VARBINARY_MAX("VARBINARY(MAX)", false, false, true, false, false, false, true, true),
VARCHAR("VARCHAR", true, false, true, false, false, true, true, true),
VARCHAR_MAX("VARCHAR(MAX)", false, false, true, false, false, true, true, true),
XML("XML", false, false, true, false, false, false, true, true),
;
private ColumnType columnType;
public static SqlServerColumnTypeEnum getByType(String dataType) {
return COLUMN_TYPE_MAP.get(dataType.toUpperCase());
}
private static Map<String, SqlServerColumnTypeEnum> COLUMN_TYPE_MAP = Maps.newHashMap();
static {
for (SqlServerColumnTypeEnum value : SqlServerColumnTypeEnum.values()) {
COLUMN_TYPE_MAP.put(value.getColumnType().getTypeName(), value);
}
}
public ColumnType getColumnType() {
return columnType;
}
SqlServerColumnTypeEnum(String dataTypeName, boolean supportLength, boolean supportScale, boolean supportNullable, boolean supportAutoIncrement, boolean supportCharset, boolean supportCollation, boolean supportComments, boolean supportDefaultValue) {
this.columnType = new ColumnType(dataTypeName, supportLength, supportScale, supportNullable, supportAutoIncrement, supportCharset, supportCollation, supportComments, supportDefaultValue, false, false, false);
}
@Override
public String buildCreateColumnSql(TableColumn column) {
SqlServerColumnTypeEnum type = this;
StringBuilder script = new StringBuilder();
script.append("[").append(column.getName()).append("]").append(" ");
script.append(buildDataType(column, type)).append(" ");
script.append(buildSparse(column, type)).append(" ");
script.append(buildDefaultValue(column, type)).append(" ");
script.append(buildNullable(column, type)).append(" ");
script.append(buildCollation(column, type)).append(" ");
return script.toString();
}
public String buildUpdateColumnSql(TableColumn column) {
SqlServerColumnTypeEnum type = this;
StringBuilder script = new StringBuilder();
script.append("[").append(column.getName()).append("]").append(" ");
script.append(buildDataType(column, type)).append(" ");
script.append(buildNullable(column, type)).append(" \ngo\n");
if (StringUtils.isNotBlank(column.getDefaultValue()) && column.getOldColumn().getDefaultValue() != null && !StringUtils.equalsIgnoreCase(column.getDefaultValue(), column.getOldColumn().getDefaultValue())) {
script.append("ALTER TABLE ").append("[").append(column.getSchemaName()).append("].[").append(column.getTableName()).append("]");
script.append(" ").append("DROP CONSTRAINT ").append("[").append(column.getDefaultConstraintName()).append("]");
script.append("ALTER TABLE ").append("[").append(column.getSchemaName()).append("].[").append(column.getTableName()).append("]");
script.append(" ").append("ADD ").append(buildDefaultValue(column, type)).append(" for ").append(column.getName()).append(" \ngo\n");
}
if (StringUtils.isNotBlank(column.getDefaultValue()) && column.getOldColumn().getDefaultValue() == null) {
script.append("ALTER TABLE ").append("[").append(column.getSchemaName()).append("].[").append(column.getTableName()).append("]");
script.append(" ").append("ADD ").append(buildDefaultValue(column, type)).append(" for ").append(column.getName()).append(" \ngo\n");
}
if (column.getSparse() != null && column.getSparse()) {
script.append("ALTER TABLE ").append("[").append(column.getSchemaName()).append("].[").append(column.getTableName()).append("]");
script.append(" ").append("ALTER COLUMN ").append("[").append(column.getName()).append("]").append(" add ").append("SPARSE").append(" \ngo\n");
}
if (StringUtils.isNotBlank(column.getCollationName())) {
script.append("ALTER TABLE ").append("[").append(column.getSchemaName()).append("].[").append(column.getTableName()).append("]");
script.append(" ").append("ALTER COLUMN ").append("[").append(column.getName()).append("]").append(" ").append("COLLATE ").append(column.getCollationName()).append(" \ngo\n");
}
return script.toString();
}
private String buildSparse(TableColumn column, SqlServerColumnTypeEnum type) {
if (Boolean.TRUE.equals(column.getSparse())) {
return "SPARSE";
} else {
return "";
}
}
private String buildCollation(TableColumn column, SqlServerColumnTypeEnum type) {
if (!type.getColumnType().isSupportCollation() || StringUtils.isEmpty(column.getCollationName())) {
return "";
}
return StringUtils.join("COLLATE ", column.getCollationName());
}
private String buildNullable(TableColumn column, SqlServerColumnTypeEnum type) {
if (!type.getColumnType().isSupportNullable()) {
return "";
}
if (column.getNullable() != null && 1 == column.getNullable()) {
return "NULL";
} else {
return "NOT NULL";
}
}
private String buildDefaultValue(TableColumn column, SqlServerColumnTypeEnum type) {
if (!type.getColumnType().isSupportDefaultValue() || StringUtils.isEmpty(column.getDefaultValue())) {
return "";
}
if ("EMPTY_STRING".equalsIgnoreCase(column.getDefaultValue().trim())) {
return StringUtils.join("DEFAULT ''");
}
if ("NULL".equalsIgnoreCase(column.getDefaultValue().trim())) {
return StringUtils.join("DEFAULT NULL");
}
return StringUtils.join("DEFAULT ", column.getDefaultValue());
}
private String buildDataType(TableColumn column, SqlServerColumnTypeEnum type) {
String columnType = type.columnType.getTypeName();
if (Arrays.asList(CHAR, NCHAR, NVARCHAR, VARBINARY, VARCHAR).contains(type)) {
StringBuilder script = new StringBuilder();
script.append(columnType);
if (column.getColumnSize() != null) {
script.append("(").append(column.getColumnSize()).append(")");
}
return script.toString();
}
if (Arrays.asList(DECIMAL, FLOAT, TIMESTAMP, TIME, DATETIME2, DATETIMEOFFSET, FLOAT, NUMERIC).contains(type)) {
StringBuilder script = new StringBuilder();
script.append(columnType);
if (column.getColumnSize() != null && column.getDecimalDigits() == null) {
script.append("(").append(column.getColumnSize()).append(")");
} else if (column.getColumnSize() != null && column.getDecimalDigits() != null) {
script.append("(").append(column.getColumnSize()).append(",").append(column.getDecimalDigits()).append(")");
}
return script.toString();
}
if (Arrays.asList().contains(type)) {
StringBuilder script = new StringBuilder();
if (column.getColumnSize() == null) {
script.append(columnType);
} else {
String[] split = columnType.split("TIMESTAMP");
script.append("TIMESTAMP").append("(").append(column.getColumnSize()).append(")").append(split[1]);
}
return script.toString();
}
return columnType;
}
private static String RENAME_COLUMN_SCRIPT = "exec sp_rename '%s.%s','%s','COLUMN' \ngo";
private String renameColumn(TableColumn tableColumn) {
return String.format(RENAME_COLUMN_SCRIPT, tableColumn.getTableName(), tableColumn.getOldName(), tableColumn.getName());
}
@Override
public String buildModifyColumn(TableColumn tableColumn) {
if (EditStatus.DELETE.name().equals(tableColumn.getEditStatus())) {
StringBuilder script = new StringBuilder();
if (StringUtils.isNotBlank(tableColumn.getDefaultConstraintName())) {
script.append("ALTER TABLE ").append("[").append(tableColumn.getSchemaName()).append("].[").append(tableColumn.getTableName()).append("]");
script.append(" ").append("DROP CONSTRAINT ").append("[").append(tableColumn.getDefaultConstraintName()).append("]");
script.append("\ngo\n");
}
script.append("ALTER TABLE ").append("[").append(tableColumn.getSchemaName()).append("].[").append(tableColumn.getTableName()).append("]");
script.append(" ").append("DROP COLUMN ").append("[").append(tableColumn.getName()).append("]");
script.append("\ngo\n");
return script.toString();
}
if (EditStatus.ADD.name().equals(tableColumn.getEditStatus())) {
StringBuilder script = new StringBuilder();
script.append("ALTER TABLE ").append("[").append(tableColumn.getSchemaName()).append("].[").append(tableColumn.getTableName()).append("]");
script.append(" ").append("ADD ").append(buildCreateColumnSql(tableColumn)).append(" \ngo\n");
return script.toString();
}
if (EditStatus.MODIFY.name().equals(tableColumn.getEditStatus())) {
StringBuilder script = new StringBuilder();
if (!StringUtils.equalsIgnoreCase(tableColumn.getOldName(), tableColumn.getName())) {
script.append(renameColumn(tableColumn));
script.append("\n");
}
script.append("ALTER TABLE ").append("[").append(tableColumn.getSchemaName()).append("].[").append(tableColumn.getTableName()).append("]");
script.append(" ").append("ALTER COLUMN ").append(buildUpdateColumnSql(tableColumn)).append(" \n");
if (StringUtils.isNotBlank(tableColumn.getComment())) {
script.append("\n").append(buildModifyColumnComment(tableColumn));
}
return script.toString();
}
return "";
}
private static String COLUMN_MODIFY_COMMENT_SCRIPT = "IF ((SELECT COUNT(*) FROM ::fn_listextendedproperty('MS_Description',\n" +
"'SCHEMA', N'%s',\n" +
"'TABLE', N'%s',\n" +
"'COLUMN', N'%s')) > 0)\n" +
" EXEC sp_updateextendedproperty\n" +
"'MS_Description', N'%s',\n" +
"'SCHEMA', N'%s',\n" +
"'TABLE', N'%s',\n" +
"'COLUMN', N'%s'\n" +
"ELSE\n" +
" EXEC sp_addextendedproperty\n" +
"'MS_Description', N'%s',\n" +
"'SCHEMA', N'%s',\n" +
"'TABLE', N'%s',\n" +
"'COLUMN', N'%s'\n go";
private String buildModifyColumnComment(TableColumn tableColumn) {
return String.format(COLUMN_MODIFY_COMMENT_SCRIPT, tableColumn.getSchemaName(), tableColumn.getTableName(),
tableColumn.getName(), tableColumn.getComment(), tableColumn.getSchemaName(), tableColumn.getTableName(), tableColumn.getName(),
tableColumn.getComment(), tableColumn.getSchemaName(), tableColumn.getTableName(), tableColumn.getName());
}
public static List<ColumnType> getTypes() {
return Arrays.stream(SqlServerColumnTypeEnum.values()).map(columnTypeEnum ->
columnTypeEnum.getColumnType()
).toList();
}
}

View File

@ -0,0 +1,119 @@
package ai.chat2db.plugin.sqlserver.type;
import ai.chat2db.spi.enums.EditStatus;
import ai.chat2db.spi.model.TableIndex;
import ai.chat2db.spi.model.TableIndexColumn;
import org.apache.commons.lang3.StringUtils;
public enum SqlServerIndexTypeEnum {
PRIMARY_KEY("Primary", "PRIMARY KEY"),
// NORMAL("Normal", "INDEX"),
//
// UNIQUE("Unique", "UNIQUE INDEX"),
UNIQUE_CLUSTERED("UNIQUE CLUSTERED", "UNIQUE CLUSTERED INDEX"),
CLUSTERED("CLUSTERED", "CLUSTERED INDEX"),
NONCLUSTERED("NONCLUSTERED", "NONCLUSTERED INDEX"),
UNIQUE_NONCLUSTERED("UNIQUE NONCLUSTERED", "UNIQUE NONCLUSTERED INDEX"),
SPATIAL("SPATIAL", "SPATIAL INDEX"),
XML("XML", "XML INDEX");
public String getName() {
return name;
}
private String name;
public String getKeyword() {
return keyword;
}
private String keyword;
SqlServerIndexTypeEnum(String name, String keyword) {
this.name = name;
this.keyword = keyword;
}
public static SqlServerIndexTypeEnum getByType(String type) {
for (SqlServerIndexTypeEnum value : SqlServerIndexTypeEnum.values()) {
if (value.name.equalsIgnoreCase(type)) {
return value;
}
}
return null;
}
//ALTER TABLE [dbo].[Employees] ADD CONSTRAINT [PK__Employee__7AD04FF164ABF7C7] PRIMARY KEY CLUSTERED ([FirstName])
public String buildIndexScript(TableIndex tableIndex) {
StringBuilder script = new StringBuilder();
if (PRIMARY_KEY.equals(this)) {
script.append("ALTER TABLE [").append(tableIndex.getSchemaName()).append("].[").append(tableIndex.getTableName()).append("] ADD CONSTRAINT ").append(buildIndexName(tableIndex)).append(" ").append(keyword).append(" ").append(buildIndexColumn(tableIndex));
} else {
script.append("CREATE ").append(keyword).append(" ");
script.append(buildIndexName(tableIndex)).append("\n ON [").append(tableIndex.getSchemaName()).append("].[").append(tableIndex.getTableName()).append("] ").append(buildIndexColumn(tableIndex));
}
script.append("\ngo");
return script.toString();
}
private String buildIndexColumn(TableIndex tableIndex) {
StringBuilder script = new StringBuilder();
script.append("(");
for (TableIndexColumn column : tableIndex.getColumnList()) {
if (StringUtils.isNotBlank(column.getColumnName())) {
script.append("[").append(column.getColumnName()).append("]");
if (!StringUtils.isBlank(column.getAscOrDesc())) {
script.append(" ").append(column.getAscOrDesc());
}
script.append(",");
}
}
script.deleteCharAt(script.length() - 1);
script.append(")");
return script.toString();
}
private String buildIndexName(TableIndex tableIndex) {
return "[" + tableIndex.getName() + "]";
}
public String buildModifyIndex(TableIndex tableIndex) {
if (EditStatus.DELETE.name().equals(tableIndex.getEditStatus())) {
return buildDropIndex(tableIndex);
}
if (EditStatus.MODIFY.name().equals(tableIndex.getEditStatus())) {
return StringUtils.join(buildDropIndex(tableIndex), "\n", buildIndexScript(tableIndex));
}
if (EditStatus.ADD.name().equals(tableIndex.getEditStatus())) {
return StringUtils.join(buildIndexScript(tableIndex));
}
return "";
}
private String buildDropIndex(TableIndex tableIndex) {
if (SqlServerIndexTypeEnum.PRIMARY_KEY.getName().equals(tableIndex.getType())) {
return StringUtils.join("ALTER TABLE [", tableIndex.getSchemaName(), "].[", tableIndex.getTableName(), "] DROP CONSTRAINT ", buildIndexName(tableIndex),"\ngo");
}
StringBuilder script = new StringBuilder();
script.append("DROP INDEX ");
script.append(buildIndexName(tableIndex));
script.append(" ON [").append(tableIndex.getSchemaName()).append("].[").append(tableIndex.getTableName()).append("] \ngo");
return script.toString();
}
}

View File

@ -0,0 +1,60 @@
package ai.chat2db.server.domain.api.param;
import ai.chat2db.spi.model.Header;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
@Data
public class UpdateSelectResultParam {
/**
* 控制台id
*/
@NotNull
private Long consoleId;
/**
* 数据源id
*/
@NotNull
private Long dataSourceId;
/**
* DB名称
*/
private String databaseName;
/**
* schema名称
*/
private String schemaName;
/**
* 展示头的列表
*/
@NotEmpty
private List<Header> headerList;
/**
* 修改后数据的列表
*/
@NotEmpty
private List<List<String>> dataList;
/**
* 数据的列表
*/
@NotEmpty
private List<List<String>> oldDataList;
/**
* 表名
*/
@NotEmpty
private String tableName;
}

View File

@ -86,6 +86,7 @@ public class TableServiceImpl implements TableService {
@Override
public ListResult<Sql> buildSql(Table oldTable, Table newTable) {
initOldTable(oldTable, newTable);
SqlBuilder sqlBuilder = Chat2DBContext.getSqlBuilder();
List<Sql> sqls = new ArrayList<>();
if (oldTable == null) {
@ -96,6 +97,19 @@ public class TableServiceImpl implements TableService {
return ListResult.of(sqls);
}
private void initOldTable(Table oldTable, Table newTable) {
if (oldTable == null) {
return;
}
Map<String, TableColumn> columnMap = oldTable.getColumnList().stream().collect(Collectors.toMap(TableColumn::getName, Function.identity()));
for (TableColumn newColumn : newTable.getColumnList()) {
TableColumn oldColumn = columnMap.get(newColumn.getName());
if (oldColumn != null) {
newColumn.setOldColumn(oldColumn);
}
}
}
@Override
public PageResult<Table> pageQuery(TablePageQueryParam param, TableSelector selector) {
MetaData metaSchema = Chat2DBContext.getMetaData();

View File

@ -4,6 +4,7 @@ import java.sql.Connection;
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.wrapper.result.DataResult;
import ai.chat2db.server.tools.base.wrapper.result.ListResult;
@ -11,6 +12,7 @@ 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.SelectResultUpdateRequest;
import ai.chat2db.server.web.api.controller.rdb.vo.ExecuteResultVO;
import ai.chat2db.spi.model.ExecuteResult;
import ai.chat2db.spi.sql.Chat2DBContext;
@ -54,6 +56,20 @@ public class RdbDmlController {
}
/**
* update 查询结果
*
* @param request
* @return
*/
@RequestMapping(value = "/execute_update", method = {RequestMethod.POST, RequestMethod.PUT})
public DataResult<String> executeUpdate(@RequestBody SelectResultUpdateRequest request) {
UpdateSelectResultParam param = rdbWebConverter.request2param(request);
return dlTemplateService.updateSelectResult(param);
}
/**
* 增删改查等数据运维
*

View File

@ -174,11 +174,13 @@ public class TableController {
tableIndex.setTableName(table.getName());
tableIndex.setDatabaseName(request.getDatabaseName());
}
return tableService.buildSql(rdbWebConverter.tableRequest2param(request.getOldTable()),table)
.map(rdbWebConverter::dto2vo);
}
/**
* 数据库支持的数据类型
*

View File

@ -2,22 +2,9 @@ package ai.chat2db.server.web.api.controller.rdb.converter;
import java.util.List;
import ai.chat2db.server.domain.api.param.DlCountParam;
import ai.chat2db.server.domain.api.param.DlExecuteParam;
import ai.chat2db.server.domain.api.param.DropParam;
import ai.chat2db.server.domain.api.param.ShowCreateTableParam;
import ai.chat2db.server.domain.api.param.TablePageQueryParam;
import ai.chat2db.server.domain.api.param.TableQueryParam;
import ai.chat2db.server.domain.api.param.*;
import ai.chat2db.server.web.api.controller.data.source.vo.DatabaseVO;
import ai.chat2db.server.web.api.controller.rdb.request.DataExportRequest;
import ai.chat2db.server.web.api.controller.rdb.request.DdlCountRequest;
import ai.chat2db.server.web.api.controller.rdb.request.DdlExportRequest;
import ai.chat2db.server.web.api.controller.rdb.request.DdlRequest;
import ai.chat2db.server.web.api.controller.rdb.request.DmlRequest;
import ai.chat2db.server.web.api.controller.rdb.request.TableBriefQueryRequest;
import ai.chat2db.server.web.api.controller.rdb.request.TableDeleteRequest;
import ai.chat2db.server.web.api.controller.rdb.request.TableDetailQueryRequest;
import ai.chat2db.server.web.api.controller.rdb.request.TableRequest;
import ai.chat2db.server.web.api.controller.rdb.request.*;
import ai.chat2db.server.web.api.controller.rdb.vo.ColumnVO;
import ai.chat2db.server.web.api.controller.rdb.vo.ExecuteResultVO;
import ai.chat2db.server.web.api.controller.rdb.vo.IndexVO;
@ -234,4 +221,7 @@ public abstract class RdbWebConverter {
public abstract List<DatabaseVO> databaseDto2vo(List<Database> dto);
public abstract MetaSchemaVO metaSchemaDto2vo(MetaSchema data);
public abstract UpdateSelectResultParam request2param(SelectResultUpdateRequest request);
}

View File

@ -0,0 +1,44 @@
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 ai.chat2db.server.web.api.controller.rdb.vo.HeaderVO;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
@Data
public class SelectResultUpdateRequest extends DataSourceBaseRequest implements DataSourceConsoleRequestInfo {
/**
* 展示头的列表
*/
private List<HeaderVO> headerList;
/**
* 修改后数据的列表
*/
private List<List<String>> dataList;
/**
* 数据的列表
*/
private List<List<String>> oldDataList;
/**
* 表名
*/
private String tableName;
/**
* 控制台id
*/
@NotNull
private Long consoleId;
@Override
public Long getConsoleId() {
return consoleId;
}
}

View File

@ -175,7 +175,6 @@ public interface MetaData {
/**
*
* @param connection
* @return
*/
@ -184,17 +183,28 @@ public interface MetaData {
/**
* Get sql builder.
*
* @return
*/
SqlBuilder getSqlBuilder();
/**
*
* @param databaseName
* @param schemaName
* @param tableName
* @return
*/
TableMeta getTableMeta(String databaseName, String schemaName, String tableName);
/**
* Get meta data name.
*
* @param names
* @return
*/
String getMetaDataName(String ...names);
}

View File

@ -16,6 +16,11 @@ import lombok.experimental.SuperBuilder;
@NoArgsConstructor
@AllArgsConstructor
public class TableColumn {
/**
* Old column, when modifying a column, you need this parameter
*/
private TableColumn oldColumn;
/**
* 旧的列名,在修改列的时候需要这个参数
* 在返回的时候oldName=name
@ -58,6 +63,8 @@ public class TableColumn {
@JsonAlias({"COLUMN_DEF"})
private String defaultValue;
/**
* 是否自增
* 为空 代表没有值 数据库的实际语义是 false
@ -166,9 +173,15 @@ public class TableColumn {
private String collationName;
//Mysql
private String value;
//ORACLE
private String unit;
// sqlserver
private Boolean sparse;
// sqlserver
private String defaultConstraintName;
}

View File

@ -9,6 +9,7 @@ import java.util.Set;
import java.util.stream.Collectors;
import ai.chat2db.server.tools.common.util.EasyIntegerUtils;
import ai.chat2db.spi.enums.DataTypeEnum;
import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLDataTypeImpl;
@ -57,6 +58,10 @@ import ai.chat2db.spi.model.TableIndexColumn;
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 org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
@ -69,6 +74,31 @@ public class SqlUtils {
public static final String DEFAULT_TABLE_NAME = "table1";
public static boolean canEdit(String sql) {
try {
Statement statement = CCJSqlParserUtil.parse(sql);
if (statement instanceof Select) {
Select select = (Select) statement;
PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
if (plainSelect.getJoins() == null && plainSelect.getFromItem() != null) {
for (SelectItem item : plainSelect.getSelectItems()) {
if (item instanceof SelectExpressionItem) {
SelectExpressionItem expressionItem = (SelectExpressionItem) item;
if (expressionItem.getAlias() != null) {
return false; // 找到了一个别名
}
}
}
return true;
}
}
} catch (Exception e) {
return false;
}
return false;
}
public static List<Sql> buildSql(Table oldTable, Table newTable) {
List<Sql> sqlList = new ArrayList<>();
// 创建表
@ -480,4 +510,11 @@ public class SqlUtils {
return list;
}
public static String getSqlValue(String value, String dataType) {
if(value == null){
return null;
}
DataTypeEnum dataTypeEnum = DataTypeEnum.getByCode(dataType);
return dataTypeEnum.getSqlValue(value);
}
}