From 8fca71781d88012495e8fb0ac4f9fb2caf0a37e2 Mon Sep 17 00:00:00 2001 From: SwallowGG <1558143046@qq.com> Date: Sun, 1 Oct 2023 22:43:16 +0800 Subject: [PATCH] support oracle table edit --- .../mysql/type/MysqlColumnTypeEnum.java | 11 +- .../plugin/mysql/type/MysqlIndexTypeEnum.java | 2 +- .../chat2db/plugin/oracle/OracleMetaData.java | 199 ++++++++++++-- .../oracle/builder/OracleSqlBuilder.java | 98 +++++++ .../oracle/type/OracleColumnTypeEnum.java | 252 ++++++++++++++++++ .../oracle/type/OracleIndexTypeEnum.java | 106 ++++++++ .../api/controller/rdb/TableController.java | 17 +- .../chat2db/spi/jdbc/DefaultMetaService.java | 15 +- .../java/ai/chat2db/spi/model/ColumnType.java | 2 + .../ai/chat2db/spi/model/TableColumn.java | 3 + 10 files changed, 666 insertions(+), 39 deletions(-) create mode 100644 chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/builder/OracleSqlBuilder.java create mode 100644 chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/type/OracleColumnTypeEnum.java create mode 100644 chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/type/OracleIndexTypeEnum.java diff --git a/chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/type/MysqlColumnTypeEnum.java b/chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/type/MysqlColumnTypeEnum.java index a34ea0bb..bd89f786 100644 --- a/chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/type/MysqlColumnTypeEnum.java +++ b/chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/type/MysqlColumnTypeEnum.java @@ -125,7 +125,7 @@ public enum MysqlColumnTypeEnum implements ColumnBuilder { MysqlColumnTypeEnum(String dataTypeName, boolean supportLength, boolean supportScale, boolean supportNullable, boolean supportAutoIncrement, boolean supportCharset, boolean supportCollation, boolean supportComments, boolean supportDefaultValue, boolean supportExtent,boolean supportValue) { - this.columnType = new ColumnType(dataTypeName, supportLength, supportScale, supportNullable, supportAutoIncrement, supportCharset, supportCollation, supportComments, supportDefaultValue, supportExtent,supportValue); + this.columnType = new ColumnType(dataTypeName, supportLength, supportScale, supportNullable, supportAutoIncrement, supportCharset, supportCollation, supportComments, supportDefaultValue, supportExtent,supportValue,false); } private static Map COLUMN_TYPE_MAP = Maps.newHashMap(); @@ -227,6 +227,15 @@ public enum MysqlColumnTypeEnum implements ColumnBuilder { 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"); + } + if(Arrays.asList(CHAR,VARCHAR,BINARY,VARBINARY, SET,ENUM).contains(type)){ return StringUtils.join("DEFAULT '",column.getDefaultValue(),"'"); } diff --git a/chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/type/MysqlIndexTypeEnum.java b/chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/type/MysqlIndexTypeEnum.java index 2c0623de..b4266434 100644 --- a/chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/type/MysqlIndexTypeEnum.java +++ b/chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/type/MysqlIndexTypeEnum.java @@ -106,6 +106,6 @@ public enum MysqlIndexTypeEnum { if (MysqlIndexTypeEnum.PRIMARY_KEY.getName().equals(tableIndex.getType())) { return StringUtils.join("DROP PRIMARY KEY"); } - return StringUtils.join("DROP INDEX `", tableIndex.getOldName()); + return StringUtils.join("DROP INDEX `", tableIndex.getOldName(),"`"); } } diff --git a/chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/OracleMetaData.java b/chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/OracleMetaData.java index 634bee8f..fa26e821 100644 --- a/chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/OracleMetaData.java +++ b/chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/OracleMetaData.java @@ -1,24 +1,28 @@ package ai.chat2db.plugin.oracle; import java.sql.Connection; +import java.sql.ResultSet; import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; +import java.util.*; +import java.util.stream.Collectors; +import ai.chat2db.plugin.oracle.builder.OracleSqlBuilder; +import ai.chat2db.plugin.oracle.type.OracleColumnTypeEnum; +import ai.chat2db.plugin.oracle.type.OracleIndexTypeEnum; import ai.chat2db.spi.MetaData; +import ai.chat2db.spi.SqlBuilder; 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 com.google.common.collect.Lists; import jakarta.validation.constraints.NotEmpty; +import org.apache.commons.lang3.StringUtils; public class OracleMetaData extends DefaultMetaService implements MetaData { @Override public String tableDDL(Connection connection, String databaseName, String schemaName, String tableName) { String sql = "select dbms_metadata.get_ddl('TABLE','" + tableName + "') as sql from dual," - + "user_tables where table_name = '" + tableName + "'"; + + "user_tables where table_name = '" + tableName + "'"; return SQLExecutor.getInstance().executeSql(connection, sql, resultSet -> { try { if (resultSet.next()) { @@ -32,15 +36,69 @@ public class OracleMetaData extends DefaultMetaService implements MetaData { }); } + private static String SELECT_TABLE_SQL = "SELECT A.OWNER, A.TABLE_NAME, B.COMMENTS " + + "FROM ALL_TABLES A LEFT JOIN ALL_TAB_COMMENTS B ON A.OWNER = B.OWNER AND A.TABLE_NAME = B.TABLE_NAME\n" + + "where A.OWNER = '%s' "; + + @Override + public List tables(Connection connection, String databaseName, String schemaName, String tableName) { + String sql = String.format(SELECT_TABLE_SQL, schemaName); + if(StringUtils.isNotBlank(tableName)){ + sql = sql + " and A.TABLE_NAME = '" + tableName + "'"; + } + return SQLExecutor.getInstance().execute(connection, sql, resultSet -> { + List
tables = new ArrayList<>(); + while (resultSet.next()) { + Table table = new Table(); + table.setDatabaseName(databaseName); + table.setSchemaName(schemaName); + table.setName(resultSet.getString("TABLE_NAME")); + table.setComment(resultSet.getString("COMMENTS")); + tables.add(table); + } + return tables; + }); + } + + private static String SELECT_TAB_COLS = "SELECT atc.column_id , atc.column_name as COLUMN_NAME, atc.data_type as DATA_TYPE , atc.data_length as DATA_LENGTH , atc.data_type_mod , atc.nullable , atc.data_default , acc.comments , atc.DATA_PRECISION , atc.DATA_SCALE , atc.CHAR_USED FROM all_tab_columns atc, all_col_comments acc WHERE atc.owner = acc.owner AND atc.table_name = acc.table_name AND atc.column_name = acc.column_name AND atc.owner = '%s' AND atc.table_name = '%s' order by atc.column_id"; + @Override + public List columns(Connection connection, String databaseName, String schemaName, String tableName) { + String sql = String.format(SELECT_TAB_COLS, schemaName, tableName); + return SQLExecutor.getInstance().execute(connection, sql, resultSet -> { + List tableColumns = new ArrayList<>(); + while (resultSet.next()) { + TableColumn tableColumn = new TableColumn(); + tableColumn.setTableName(tableName); + tableColumn.setSchemaName(schemaName); + tableColumn.setName(resultSet.getString("COLUMN_NAME")); + tableColumn.setColumnType(resultSet.getString("DATA_TYPE")); + tableColumn.setColumnSize(resultSet.getInt("DATA_LENGTH")); + tableColumn.setDefaultValue(resultSet.getString("DATA_DEFAULT")); + tableColumn.setComment(resultSet.getString("COMMENTS")); + tableColumn.setNullable("Y".equalsIgnoreCase(resultSet.getString("NULLABLE"))?1:0); + tableColumn.setOrdinalPosition(resultSet.getInt("COLUMN_ID")); + tableColumn.setDecimalDigits(resultSet.getInt("DATA_SCALE")); + String charUsed = resultSet.getString("CHAR_USED"); + if("B".equalsIgnoreCase(charUsed)){ + tableColumn.setUnit("BYTE"); + }else if("C".equalsIgnoreCase(charUsed)){ + tableColumn.setUnit("CHAR"); + } + tableColumns.add(tableColumn); + } + return tableColumns; + }); + } + private static String ROUTINES_SQL - = "SELECT LINE, TEXT " - + "FROM ALL_SOURCE " - + "WHERE TYPE = '%s' AND NAME = '%s' " - + "ORDER BY LINE"; + = "SELECT LINE, TEXT " + + "FROM ALL_SOURCE " + + "WHERE TYPE = '%s' AND NAME = '%s' " + + "ORDER BY LINE"; @Override public Function function(Connection connection, @NotEmpty String databaseName, String schemaName, - String functionName) { + String functionName) { String sql = String.format(ROUTINES_SQL, "FUNCTION", functionName); return SQLExecutor.getInstance().execute(connection, sql, resultSet -> { @@ -60,28 +118,101 @@ public class OracleMetaData extends DefaultMetaService implements MetaData { } private static String TRIGGER_SQL_LIST - = "SELECT TRIGGER_NAME " - + "FROM ALL_TRIGGERS WHERE OWNER = '%s'"; + = "SELECT TRIGGER_NAME " + + "FROM ALL_TRIGGERS WHERE OWNER = '%s'"; + + private static String SELECT_PK_SQL = "select acc.CONSTRAINT_NAME from all_cons_columns acc, all_constraints ac where acc.constraint_name = ac.constraint_name and acc.owner = ac.owner and acc.owner = '%s' and ac.constraint_type = 'P' and ac.table_name = '%s' "; + + private static String SELECT_TABLE_INDEX = "SELECT ai.index_name AS Key_name, aic.column_name AS Column_name, ai.index_type AS Index_type, ai.uniqueness AS Unique_name, aic.COLUMN_POSITION as Seq_in_index, aic.descend AS Collation, ex.COLUMN_EXPRESSION as COLUMN_EXPRESSION FROM all_ind_columns aic JOIN all_indexes ai ON aic.table_owner = ai.table_owner and aic.table_name = ai.table_name and aic.index_name = ai.index_name LEFT JOIN ALL_IND_EXPRESSIONS ex ON aic.table_owner = ex.table_owner and aic.table_name = ex.table_name and aic.index_name = ex.index_name where ai.table_owner = '%s' AND ai.table_name = '%s' "; + + + @Override + public List indexes(Connection connection, String databaseName, String schemaName, String tableName) { + String pkSql = String.format(SELECT_PK_SQL, schemaName, tableName); + Set pkSet = new HashSet<>(); + SQLExecutor.getInstance().execute(connection, pkSql, resultSet -> { + while (resultSet.next()) { + pkSet.add(resultSet.getString("CONSTRAINT_NAME")); + } + return null; + } + ); + + String sql = String.format(SELECT_TABLE_INDEX, schemaName, tableName); + return SQLExecutor.getInstance().execute(connection, sql, resultSet -> { + LinkedHashMap map = new LinkedHashMap(); + while (resultSet.next()) { + String keyName = resultSet.getString("Key_name"); + TableIndex tableIndex = map.get(keyName); + if (tableIndex != null) { + List columnList = tableIndex.getColumnList(); + columnList.add(getTableIndexColumn(resultSet)); + columnList = columnList.stream().sorted(Comparator.comparing(TableIndexColumn::getOrdinalPosition)) + .collect(Collectors.toList()); + tableIndex.setColumnList(columnList); + } else { + TableIndex index = new TableIndex(); + index.setDatabaseName(databaseName); + index.setSchemaName(schemaName); + index.setTableName(tableName); + index.setName(keyName); + index.setUnique("unique".equalsIgnoreCase(resultSet.getString("Unique_name"))); + index.setType(resultSet.getString("Index_type")); + List tableIndexColumns = new ArrayList<>(); + tableIndexColumns.add(getTableIndexColumn(resultSet)); + index.setColumnList(tableIndexColumns); + if (index.getUnique()) { + index.setType(OracleIndexTypeEnum.UNIQUE.getName()); + } else if ("NORMAL".equalsIgnoreCase(index.getType())) { + index.setType(OracleIndexTypeEnum.NORMAL.getName()); + } else if ("BITMAP".equalsIgnoreCase(index.getType())) { + index.setType(OracleIndexTypeEnum.BITMAP.getName()); + } else if(StringUtils.isNotBlank(index.getType()) && index.getType().toUpperCase().contains("NORMAL")){ + index.setType(OracleIndexTypeEnum.NORMAL.getName()); + } + if(pkSet.contains(keyName)){ + index.setType(OracleIndexTypeEnum.PRIMARY_KEY.getName()); + } + map.put(keyName, index); + } + } + return map.values().stream().collect(Collectors.toList()); + }); + + } + + private TableIndexColumn getTableIndexColumn(ResultSet resultSet) throws SQLException { + TableIndexColumn tableIndexColumn = new TableIndexColumn(); + tableIndexColumn.setColumnName(resultSet.getString("Column_name")); + String expression = resultSet.getString("COLUMN_EXPRESSION"); + if(!StringUtils.isBlank(expression)){ + tableIndexColumn.setColumnName(expression.replace("\"","")); + } + tableIndexColumn.setOrdinalPosition(resultSet.getShort("Seq_in_index")); + tableIndexColumn.setCollation(resultSet.getString("Collation")); + tableIndexColumn.setAscOrDesc(resultSet.getString("Collation")); + return tableIndexColumn; + } @Override public List triggers(Connection connection, String databaseName, String schemaName) { List triggers = new ArrayList<>(); return SQLExecutor.getInstance().execute(connection, String.format(TRIGGER_SQL_LIST, schemaName), - 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; - }); + 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 triggerName) { String sql = String.format(ROUTINES_SQL, "TRIGGER", triggerName); return SQLExecutor.getInstance().execute(connection, sql, resultSet -> { @@ -100,7 +231,7 @@ public class OracleMetaData extends DefaultMetaService implements MetaData { @Override public Procedure procedure(Connection connection, @NotEmpty String databaseName, String schemaName, - String procedureName) { + String procedureName) { String sql = String.format(ROUTINES_SQL, "PROCEDURE", procedureName); return SQLExecutor.getInstance().execute(connection, sql, resultSet -> { StringBuilder sb = new StringBuilder(); @@ -117,7 +248,7 @@ public class OracleMetaData extends DefaultMetaService implements MetaData { } private static String VIEW_SQL - = "SELECT VIEW_NAME, TEXT FROM ALL_VIEWS WHERE OWNER = '%s' AND VIEW_NAME = '%s'"; + = "SELECT VIEW_NAME, TEXT FROM ALL_VIEWS WHERE OWNER = '%s' AND VIEW_NAME = '%s'"; @Override public Table view(Connection connection, String databaseName, String schemaName, String viewName) { @@ -133,4 +264,18 @@ public class OracleMetaData extends DefaultMetaService implements MetaData { return table; }); } + + @Override + public SqlBuilder getSqlBuilder() { + return new OracleSqlBuilder(); + } + + @Override + public TableMeta getTableMeta(String databaseName, String schemaName, String tableName) { + return TableMeta.builder() + .columnTypes(OracleColumnTypeEnum.getTypes()) + .charsets(Lists.newArrayList()) + .collations(Lists.newArrayList()) + .build(); + } } diff --git a/chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/builder/OracleSqlBuilder.java b/chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/builder/OracleSqlBuilder.java new file mode 100644 index 00000000..7394d100 --- /dev/null +++ b/chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/builder/OracleSqlBuilder.java @@ -0,0 +1,98 @@ +package ai.chat2db.plugin.oracle.builder; + +import ai.chat2db.plugin.oracle.type.OracleColumnTypeEnum; +import ai.chat2db.plugin.oracle.type.OracleIndexTypeEnum; +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 OracleSqlBuilder 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; + } + OracleColumnTypeEnum typeEnum = OracleColumnTypeEnum.getByType(column.getColumnType()); + script.append("\t").append(typeEnum.buildCreateColumnSql(column)).append(",\n"); + } + + script = new StringBuilder(script.substring(0, script.length() - 2)); + script.append("\n);"); + + for (TableIndex tableIndex : table.getIndexList()) { + if(StringUtils.isBlank(tableIndex.getName())|| StringUtils.isBlank(tableIndex.getType())){ + continue; + } + OracleIndexTypeEnum oracleColumnTypeEnum = OracleIndexTypeEnum.getByType(tableIndex.getType()); + script.append("\n").append("").append(oracleColumnTypeEnum.buildIndexScript(tableIndex)).append(";"); + } + + for (TableColumn column : table.getColumnList()) { + if(StringUtils.isBlank(column.getName())|| StringUtils.isBlank(column.getColumnType()) || StringUtils.isBlank(column.getComment())){ + continue; + } + script.append("\n").append(buildComment(column)).append(";"); + } + + if (StringUtils.isNotBlank(table.getComment())) { + script.append("\n").append(buildTableComment(table)).append(";"); + } + + + return script.toString(); + } + + private String buildTableComment(Table table) { + StringBuilder script = new StringBuilder(); + script.append("COMMENT ON TABLE ").append("\"").append(table.getSchemaName()).append("\".\"").append(table.getName()).append("\" IS '").append(table.getComment()).append("'"); + return script.toString(); + } + + private String buildComment(TableColumn column) { + StringBuilder script = new StringBuilder(); + script.append("COMMENT ON COLUMN ").append("\"").append(column.getSchemaName()).append("\".\"").append(column.getTableName()).append("\".\"").append(column.getName()).append("\" IS '").append(column.getComment()).append("'"); + return script.toString(); + } + + @Override + public String buildModifyTaleSql(Table oldTable, Table newTable) { + StringBuilder script = new StringBuilder(); + + if (!StringUtils.equalsIgnoreCase(oldTable.getName(), newTable.getName())) { + script.append("ALTER TABLE "). append("\"").append(oldTable.getSchemaName()).append("\"").append("").append("\"").append(oldTable.getName()).append("\""); + script.append(" ").append("RENAME TO ").append("\"").append(newTable.getName()).append("\"").append(";"); + } + if (!StringUtils.equalsIgnoreCase(oldTable.getComment(), newTable.getComment())) { + script.append("\n").append(buildTableComment(newTable)).append(";"); + } + + + // append modify column + for (TableColumn tableColumn : newTable.getColumnList()) { + if (StringUtils.isNotBlank(tableColumn.getEditStatus())) { + OracleColumnTypeEnum typeEnum = OracleColumnTypeEnum.getByType(tableColumn.getColumnType()); + script.append("\t").append(typeEnum.buildModifyColumn(tableColumn)).append(";\n"); + } + } + + // append modify index + for (TableIndex tableIndex : newTable.getIndexList()) { + if (StringUtils.isNotBlank(tableIndex.getEditStatus()) && StringUtils.isNotBlank(tableIndex.getType())) { + OracleIndexTypeEnum mysqlIndexTypeEnum = OracleIndexTypeEnum.getByType(tableIndex.getType()); + script.append("\t").append(mysqlIndexTypeEnum.buildModifyIndex(tableIndex)).append(";\n"); + } + } + + script = new StringBuilder(script.substring(0, script.length() - 2)); + script.append(";"); + + return script.toString(); + } +} diff --git a/chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/type/OracleColumnTypeEnum.java b/chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/type/OracleColumnTypeEnum.java new file mode 100644 index 00000000..f80ac88f --- /dev/null +++ b/chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/type/OracleColumnTypeEnum.java @@ -0,0 +1,252 @@ +package ai.chat2db.plugin.oracle.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 OracleColumnTypeEnum implements ColumnBuilder { + //JSON("JSON", false, false, true, false, false, false, true, false, false, false) + + BFILE("BFILE", false, false, true, false, false, false, true, true, false, false), + + BINARY_DOUBLE("BINARY_DOUBLE", false, false, true, false, false, false, true, true, false, false), + + + BINARY_FLOAT("BINARY_FLOAT", false, false, true, false, false, false, true, true, false, false), + + + BLOB("BLOB", false, false, true, false, false, false, true, true, false, false), + + + CHAR("CHAR", true, false, true, false, false, false, true, true, false, true), + + CHAR_VARYING("CHAR VARYING", true, false, true, false, false, false, true, true, false, true), + + CHARACTER("CHARACTER", true, false, true, false, false, false, true, true, false, true), + + CHARACTER_VARYING("CHARACTER VARYING", true, false, true, false, false, false, true, true, false, true), + + CLOB("CLOB", false, false, true, false, false, false, true, true, false, false), + + DATE("DATE", false, false, true, false, false, false, true, true, false, false), + + DECIMAL("DECIMAL", true, true, true, false, false, false, true, true, false, false), + + DOUBLE_PRECISION("DOUBLE PRECISION", false, false, true, false, false, false, true, true, false, false), + + + FLOAT("FLOAT", true, false, true, false, false, false, true, true, false, false), + + INT("INT", false, false, true, false, false, false, true, true, false, false), + + INTEGER("INTEGER", false, false, true, false, false, false, true, true, false, false), + + LONG("LONG", false, false, true, false, false, false, true, true, false, false), + + LONG_RAW("LONG RAW", false, false, true, false, false, false, true, true, false, false), + + + LONG_VARCHAR("LONG VARCHAR", false, false, true, false, false, false, true, true, false, false), + + NATIONAL_CHAR("NATIONAL CHAR", true, false, true, false, false, false, true, true, false, true), + + + NATIONAL_CHAR_VARYING("NATIONAL CHAR VARYING", true, false, true, false, false, false, true, true, false, true), + + + NATIONAL_CHARACTER("NATIONAL CHARACTER", true, false, true, false, false, false, true, true, false, true), + + + NATIONAL_CHARACTER_VARYING("NATIONAL CHARACTER VARYING", true, false, true, false, false, false, true, true, false, true), + + NCHAR("NCHAR", true, false, true, false, false, false, true, true, false, false), + + NCHAR_VARYING("NCHAR VARYING", true, false, true, false, false, false, true, true, false, false), + + NCLOB("NCLOB", false, false, true, false, false, false, true, true, false, false), + + NUMBER("NUMBER", true, true, true, false, false, false, true, true, false, false), + + + NVARCHAR2("NVARCHAR2", true, false, true, false, false, false, true, true, false, true), + + RAW("RAW", true, false, true, false, false, false, true, true, false, false), + + REAL("REAL", false, false, true, false, false, false, true, true, false, false), + + ROWID("ROWID", false, false, true, false, false, false, true, true, false, false), + + + SMALLINT("SMALLINT", false, false, true, false, false, false, true, true, false, false), + + TIMESTAMP("TIMESTAMP", true, false, true, false, false, false, true, true, false, false), + + TIMESTAMP_WITH_LOCAL_TIME_ZONE("TIMESTAMP WITH LOCAL TIME ZONE", true, false, true, false, false, false, true, true, false, false), + + + TIMESTAMP_WITH_TIME_ZONE("TIMESTAMP WITH TIME ZONE", true, false, true, false, false, false, true, true, false, false), + + UROWID("UROWID", true, false, true, false, false, false, true, true, false, false), + + VARCHAR("VARCHAR", true, false, true, false, false, false, true, true, false, true), + + VARCHAR2("VARCHAR2", true, false, true, false, false, false, true, true, false, true), + + ; + private ColumnType columnType; + + public static OracleColumnTypeEnum getByType(String dataType) { + return COLUMN_TYPE_MAP.get(dataType.toUpperCase()); + } + + private static Map COLUMN_TYPE_MAP = Maps.newHashMap(); + + static { + for (OracleColumnTypeEnum value : OracleColumnTypeEnum.values()) { + COLUMN_TYPE_MAP.put(value.getColumnType().getTypeName(), value); + } + } + + public ColumnType getColumnType() { + return columnType; + } + + + OracleColumnTypeEnum(String dataTypeName, boolean supportLength, boolean supportScale, boolean supportNullable, boolean supportAutoIncrement, boolean supportCharset, boolean supportCollation, boolean supportComments, boolean supportDefaultValue, boolean supportExtent, boolean supportUnit) { + this.columnType = new ColumnType(dataTypeName, supportLength, supportScale, supportNullable, supportAutoIncrement, supportCharset, supportCollation, supportComments, supportDefaultValue, supportExtent, false, supportUnit); + } + + @Override + public String buildCreateColumnSql(TableColumn column) { + OracleColumnTypeEnum type = COLUMN_TYPE_MAP.get(column.getColumnType().toUpperCase()); + if (type == null) { + return ""; + } + StringBuilder script = new StringBuilder(); + + script.append("\"").append(column.getName()).append("\"").append(" "); + + script.append(buildDataType(column, type)).append(" "); + + script.append(buildDefaultValue(column,type)).append(" "); + + script.append(buildNullable(column, type)).append(" "); + + return script.toString(); + } + + + private String buildNullable(TableColumn column,OracleColumnTypeEnum type) { + if(!type.getColumnType().isSupportNullable()){ + return ""; + } + if (column.getNullable()!=null && 1==column.getNullable()) { + return "NULL"; + } else { + return "NOT NULL"; + } + } + + private String buildDefaultValue(TableColumn column, OracleColumnTypeEnum 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, OracleColumnTypeEnum type) { + String columnType = type.columnType.getTypeName(); + if (Arrays.asList(CHAR, CHAR_VARYING, CHARACTER, CHARACTER_VARYING, + NVARCHAR2, VARCHAR, VARCHAR2,NATIONAL_CHAR, + NATIONAL_CHAR_VARYING,NATIONAL_CHARACTER, + NATIONAL_CHARACTER_VARYING,NCHAR,NCHAR_VARYING).contains(type)) { + StringBuilder script = new StringBuilder(); + script.append(columnType); + if (column.getColumnSize() != null && StringUtils.isEmpty(column.getUnit())) { + script.append("(").append(column.getColumnSize()).append(")"); + } else if (column.getColumnSize() != null && !StringUtils.isEmpty(column.getUnit())) { + script.append("(").append(column.getColumnSize()).append(" ").append(column.getUnit()).append(")"); + } + return script.toString(); + } + + if (Arrays.asList(DECIMAL, FLOAT, NUMBER, UROWID,RAW,TIMESTAMP).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(TIMESTAMP_WITH_TIME_ZONE,TIMESTAMP_WITH_LOCAL_TIME_ZONE).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; + } + + + + @Override + public String buildModifyColumn(TableColumn tableColumn) { + + if (EditStatus.DELETE.name().equals(tableColumn.getEditStatus())) { + StringBuilder script = new StringBuilder(); + script.append("ALTER TABLE "). append("\"").append(tableColumn.getSchemaName()).append("\"").append("").append("\"").append(tableColumn.getTableName()).append("\""); + script.append(" ").append("DROP COLUMN ").append("\"").append(tableColumn.getName()).append("\"").append(";"); + return script.toString(); + } + if (EditStatus.ADD.name().equals(tableColumn.getEditStatus())) { + StringBuilder script = new StringBuilder(); + script.append("ALTER TABLE "). append("\"").append(tableColumn.getSchemaName()).append("\"").append("").append("\"").append(tableColumn.getTableName()).append("\""); + script.append(" ").append("ADD (").append(buildCreateColumnSql(tableColumn)).append(");"); + return script.toString(); + } + if (EditStatus.MODIFY.name().equals(tableColumn.getEditStatus())) { + StringBuilder script = new StringBuilder(); + script.append("ALTER TABLE "). append("\"").append(tableColumn.getSchemaName()).append("\"").append("").append("\"").append(tableColumn.getTableName()).append("\""); + script.append(" ").append("MODIFY (").append(buildCreateColumnSql(tableColumn)).append("); \n" ); + + if (!StringUtils.equalsIgnoreCase(tableColumn.getOldName(), tableColumn.getName())) { + script.append("ALTER TABLE "). append("\"").append(tableColumn.getSchemaName()).append("\"").append("").append("\"").append(tableColumn.getTableName()).append("\""); + script.append(" ").append("RENAME COLUMN ").append("\"").append(tableColumn.getOldName()).append("\"").append(" TO ").append("\"").append(tableColumn.getName()).append("\"").append(";"); + + } + return script.toString(); + + } + return ""; + } + public static List getTypes(){ + return Arrays.stream(OracleColumnTypeEnum.values()).map(columnTypeEnum -> + columnTypeEnum.getColumnType() + ).toList(); + } +} diff --git a/chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/type/OracleIndexTypeEnum.java b/chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/type/OracleIndexTypeEnum.java new file mode 100644 index 00000000..f0f183f1 --- /dev/null +++ b/chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/type/OracleIndexTypeEnum.java @@ -0,0 +1,106 @@ +package ai.chat2db.plugin.oracle.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 OracleIndexTypeEnum { + + PRIMARY_KEY("Primary", "PRIMARY KEY"), + + NORMAL("Normal", "INDEX"), + + UNIQUE("Unique", "UNIQUE INDEX"), + + BITMAP("BITMAP", "BITMAP INDEX"); + + + public String getName() { + return name; + } + + private String name; + + + public String getKeyword() { + return keyword; + } + + private String keyword; + + OracleIndexTypeEnum(String name, String keyword) { + this.name = name; + this.keyword = keyword; + } + + + public static OracleIndexTypeEnum getByType(String type) { + for (OracleIndexTypeEnum value : OracleIndexTypeEnum.values()) { + if (value.name.equalsIgnoreCase(type)) { + return value; + } + } + return null; + } + + public String buildIndexScript(TableIndex tableIndex) { + StringBuilder script = new StringBuilder(); + if (PRIMARY_KEY.equals(this)) { + script.append("CREATE PRIMARY KEY "); + } else if (UNIQUE.equals(this)) { + script.append("CREATE UNIQUE INDEX "); + } else { + script.append("CREATE INDEX "); + } + script.append(buildIndexName(tableIndex)).append(" ON \"").append(tableIndex.getTableName()).append("\" ").append(buildIndexColumn(tableIndex)); + + 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.getSchemaName() + "\"" + "\"" + 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 (OracleIndexTypeEnum.PRIMARY_KEY.getName().equals(tableIndex.getType())) { + return StringUtils.join("DROP PRIMARY KEY"); + } + StringBuilder script = new StringBuilder(); + script.append("DROP INDEX "); + script.append(buildIndexName(tableIndex)); + + return script.toString(); + } +} diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/TableController.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/TableController.java index ac985e8e..bcbcca69 100644 --- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/TableController.java +++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/TableController.java @@ -161,9 +161,20 @@ public class TableController { */ @PostMapping("/modify/sql") public ListResult modifySql(@Valid @RequestBody TableModifySqlRequest request) { - return tableService.buildSql( - rdbWebConverter.tableRequest2param(request.getOldTable()), - rdbWebConverter.tableRequest2param(request.getNewTable())) + Table table = rdbWebConverter.tableRequest2param(request.getNewTable()); + table.setSchemaName(request.getSchemaName()); + table.setDatabaseName(request.getDatabaseName()); + for (TableColumn tableColumn : table.getColumnList()) { + tableColumn.setSchemaName(request.getSchemaName()); + tableColumn.setTableName(table.getName()); + tableColumn.setDatabaseName(request.getDatabaseName()); + } + for (TableIndex tableIndex : table.getIndexList()) { + tableIndex.setSchemaName(request.getSchemaName()); + tableIndex.setTableName(table.getName()); + tableIndex.setDatabaseName(request.getDatabaseName()); + } + return tableService.buildSql(rdbWebConverter.tableRequest2param(request.getOldTable()),table) .map(rdbWebConverter::dto2vo); } diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/jdbc/DefaultMetaService.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/jdbc/DefaultMetaService.java index ca2ca8eb..bdd4aa78 100644 --- a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/jdbc/DefaultMetaService.java +++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/jdbc/DefaultMetaService.java @@ -8,6 +8,7 @@ import ai.chat2db.spi.MetaData; import ai.chat2db.spi.SqlBuilder; import ai.chat2db.spi.model.*; import ai.chat2db.spi.sql.SQLExecutor; +import org.apache.commons.lang3.StringUtils; /** * @author jipengfei @@ -31,7 +32,7 @@ public class DefaultMetaService implements MetaData { @Override public List
tables(Connection connection, String databaseName, String schemaName, String tableName) { - return SQLExecutor.getInstance().tables(connection, databaseName, schemaName, tableName, new String[]{"TABLE"}); + return SQLExecutor.getInstance().tables(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName, tableName, new String[]{"TABLE"}); } @Override @@ -41,12 +42,12 @@ public class DefaultMetaService implements MetaData { @Override public List
views(Connection connection, String databaseName, String schemaName) { - return SQLExecutor.getInstance().tables(connection, databaseName, schemaName, null, new String[]{"VIEW"}); + return SQLExecutor.getInstance().tables(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName, null, new String[]{"VIEW"}); } @Override public List functions(Connection connection, String databaseName, String schemaName) { - return SQLExecutor.getInstance().functions(connection, databaseName, schemaName); + return SQLExecutor.getInstance().functions(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName); } @Override @@ -56,23 +57,23 @@ public class DefaultMetaService implements MetaData { @Override public List procedures(Connection connection, String databaseName, String schemaName) { - return SQLExecutor.getInstance().procedures(connection, databaseName, schemaName); + return SQLExecutor.getInstance().procedures(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName); } @Override public List columns(Connection connection, String databaseName, String schemaName, String tableName) { - return SQLExecutor.getInstance().columns(connection, databaseName, schemaName, tableName, null); + return SQLExecutor.getInstance().columns(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName, tableName, null); } @Override public List columns(Connection connection, String databaseName, String schemaName, String tableName, String columnName) { - return SQLExecutor.getInstance().columns(connection, databaseName, schemaName, tableName, columnName); + return SQLExecutor.getInstance().columns(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName, tableName, columnName); } @Override public List indexes(Connection connection, String databaseName, String schemaName, String tableName) { - return SQLExecutor.getInstance().indexes(connection, databaseName, schemaName, tableName); + return SQLExecutor.getInstance().indexes(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName, tableName); } @Override diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/ColumnType.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/ColumnType.java index 715b8200..5d4862f3 100644 --- a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/ColumnType.java +++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/ColumnType.java @@ -19,4 +19,6 @@ public class ColumnType { private boolean supportExtent; private boolean supportValue; + + private boolean supportUnit; } diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/TableColumn.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/TableColumn.java index 13588a12..91ede341 100644 --- a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/TableColumn.java +++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/TableColumn.java @@ -168,4 +168,7 @@ public class TableColumn { private String value; + + private String unit; + }