diff --git a/chat2db-client/src/components/ConnectionEdit/config/dataSource.ts b/chat2db-client/src/components/ConnectionEdit/config/dataSource.ts index f14c86510..2b974edbf 100644 --- a/chat2db-client/src/components/ConnectionEdit/config/dataSource.ts +++ b/chat2db-client/src/components/ConnectionEdit/config/dataSource.ts @@ -2080,4 +2080,125 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [ extendInfo: [], type: DatabaseTypeCode.MONGODB }, + //MONGODB + { + baseInfo: { + items: [ + { + defaultValue: '@localhost', + inputType: InputType.INPUT, + labelNameCN: '名称', + labelNameEN: 'Name', + name: 'alias', + required: true, + styles: { + width: '100%', + } + }, + envItem, + { + defaultValue: 'localhost', + inputType: InputType.INPUT, + labelNameCN: '主机', + labelNameEN: 'Host', + name: 'host', + required: true, + styles: { + width: '70%', + } + }, + { + defaultValue: '5138', + inputType: InputType.INPUT, + labelNameCN: '端口', + labelNameEN: 'Port', + name: 'port', + labelTextAlign: 'right', + required: true, + styles: { + width: '30%', + labelWidthEN: '40px', + labelWidthCN: '40px', + labelAlign: 'right' + } + }, + { + defaultValue: AuthenticationType.USERANDPASSWORD, + inputType: InputType.SELECT, + labelNameCN: '身份验证', + labelNameEN: 'Authentication', + name: 'authenticationType', + required: true, + selects: [ + { + items: [ + { + defaultValue: 'root', + inputType: InputType.INPUT, + labelNameCN: '用户名', + labelNameEN: 'User', + name: 'user', + required: true, + styles: { + width: '100%', + } + }, + { + defaultValue: '', + inputType: InputType.PASSWORD, + labelNameCN: '密码', + labelNameEN: 'Password', + name: 'password', + required: true, + styles: { + width: '100%', + } + }, + ], + label: 'User&Password', + value: AuthenticationType.USERANDPASSWORD, + }, + { + label: 'NONE', + value: AuthenticationType.NONE, + items: [], + + }, + ], + styles: { + width: '50%', + } + }, + { + defaultValue: '', + inputType: InputType.INPUT, + labelNameCN: '数据库', + labelNameEN: 'Database', + name: 'database', + required: false, + styles: { + width: '100%', + } + }, + { + defaultValue: 'jdbc:xugu://localhost:5138', + inputType: InputType.INPUT, + labelNameCN: 'URL', + labelNameEN: 'URL', + name: 'url', + required: true, + styles: { + width: '100%', + } + }, + + ], + pattern: /jdbc:xugu:\/\/(.*):(\d+)(\/(\w+))?/, + template: 'jdbc:xugu://{host}:{port}/{database}', + //excludes: [OperationColumn.ViewDDL, OperationColumn.CreateTable,OperationColumn.EditTable] + }, + ssh: sshConfig, + extendInfo: [], + type: DatabaseTypeCode.XUGUDB + }, ]; diff --git a/chat2db-client/src/constants/common.ts b/chat2db-client/src/constants/common.ts index b6f2339be..26e4156e5 100644 --- a/chat2db-client/src/constants/common.ts +++ b/chat2db-client/src/constants/common.ts @@ -15,6 +15,7 @@ export enum DatabaseTypeCode { PRESTO = "PRESTO", HIVE = "HIVE", KINGBASE = "KINGBASE", + XUGUDB = "XUGUDB", } export enum ConsoleStatus { diff --git a/chat2db-client/src/constants/database.ts b/chat2db-client/src/constants/database.ts index ba7c3c75c..1ba66b063 100644 --- a/chat2db-client/src/constants/database.ts +++ b/chat2db-client/src/constants/database.ts @@ -118,6 +118,13 @@ export const databaseMap: { // port: 27017, icon: '\uec21', }, + [DatabaseTypeCode.XUGUDB]: { + name: 'XuguDB', + img: moreDBLogo, + code: DatabaseTypeCode.XUGUDB, + // port: 5138, + icon: '\uec21', + }, // [DatabaseTypeCode.REDIS]: { // name: 'Redis', // img: moreDBLogo, diff --git a/chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/HiveMetaData.java b/chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/HiveMetaData.java index 59e62955c..0579d4876 100644 --- a/chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/HiveMetaData.java +++ b/chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/HiveMetaData.java @@ -68,7 +68,7 @@ public String tableDDL(Connection connection, @NotEmpty String databaseName, Str @Override public String getMetaDataName(String... names) { - return Arrays.stream(names).filter(name -> StringUtils.isNotBlank(name)).map(name -> "`" + name + "`").collect(Collectors.joining(".")); + return Arrays.stream(names).skip(1).filter(name -> StringUtils.isNotBlank(name)).map(name -> "`" + name + "`").collect(Collectors.joining(".")); } @Override diff --git a/chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/builder/HiveSqlBuilder.java b/chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/builder/HiveSqlBuilder.java index 66d918558..fc690bcf8 100644 --- a/chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/builder/HiveSqlBuilder.java +++ b/chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/builder/HiveSqlBuilder.java @@ -2,21 +2,15 @@ import ai.chat2db.plugin.hive.type.HiveColumnTypeEnum; import ai.chat2db.plugin.hive.type.HiveIndexTypeEnum; -import ai.chat2db.spi.SqlBuilder; import ai.chat2db.spi.jdbc.DefaultSqlBuilder; import ai.chat2db.spi.model.Database; import ai.chat2db.spi.model.Table; import ai.chat2db.spi.model.TableColumn; import ai.chat2db.spi.model.TableIndex; -import cn.hutool.core.util.ArrayUtil; import org.apache.commons.lang3.StringUtils; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -public class HiveSqlBuilder extends DefaultSqlBuilder implements SqlBuilder { +public class HiveSqlBuilder extends DefaultSqlBuilder { @Override public String buildCreateTableSql(Table table) { StringBuilder script = new StringBuilder(); @@ -117,7 +111,7 @@ public String buildModifyTaleSql(Table oldTable, Table newTable) { } // append reorder column - script.append(buildGenerateReorderColumnSql(oldTable, newTable)); + // script.append(buildGenerateReorderColumnSql(oldTable, newTable)); if (script.length() > 2) { script = new StringBuilder(script.substring(0, script.length() - 2)); @@ -156,142 +150,4 @@ public String buildCreateDatabaseSql(Database database) { return sqlBuilder.toString(); } - public String buildGenerateReorderColumnSql(Table oldTable, Table newTable) { - StringBuilder sql = new StringBuilder(); - int n = 0; - // Create a map to store the index of each column in the old table's column list - Map oldColumnIndexMap = new HashMap<>(); - for (int i = 0; i < oldTable.getColumnList().size(); i++) { - oldColumnIndexMap.put(oldTable.getColumnList().get(i).getName(), i); - } - String[] oldColumnArray = oldTable.getColumnList().stream().map(TableColumn::getName).toArray(String[]::new); - String[] newColumnArray = newTable.getColumnList().stream().map(TableColumn::getName).toArray(String[]::new); - - buildSql(oldColumnArray, newColumnArray, sql, oldTable, newTable, n); - - return sql.toString(); - } - - private String[] buildSql(String[] originalArray, String[] targetArray, StringBuilder sql, Table oldTable, Table newTable, int n) { - // 先完成首位移动 - if (!originalArray[0].equals(targetArray[0])) { - int a = findIndex(originalArray, targetArray[0]); - TableColumn column = oldTable.getColumnList().stream().filter(col -> StringUtils.equals(col.getName(), originalArray[a])).findFirst().get(); - String[] newArray = moveElement(originalArray, a, 0); - System.out.println(ArrayUtil.toString(newArray)); - sql.append(" MODIFY COLUMN "); - HiveColumnTypeEnum typeEnum = HiveColumnTypeEnum.getByType(column.getColumnType()); - sql.append(typeEnum.buildColumn(column)); - sql.append(" FIRST;\n"); - n++; - if (Arrays.equals(newArray, targetArray)) { - return newArray; - } - String[] resultArray = buildSql(newArray, targetArray, sql, oldTable, newTable, n); - if (Arrays.equals(resultArray, targetArray)) { - return resultArray; - } - } - - // 在完成最后一位移动 - int max = originalArray.length - 1; - if (!originalArray[max].equals(targetArray[max])) { - int a = findIndex(originalArray, targetArray[max]); - //System.out.println("Move " + originalArray[a] + " after " + (a > 0 ? originalArray[max] : "start")); - TableColumn column = oldTable.getColumnList().stream().filter(col -> StringUtils.equals(col.getName(), originalArray[a])).findFirst().get(); - String[] newArray = moveElement(originalArray, a, max); - System.out.println(ArrayUtil.toString(newArray)); - if (n > 0) { - sql.append("ALTER TABLE "); - if (StringUtils.isNotBlank(oldTable.getDatabaseName())) { - sql.append("`").append(oldTable.getDatabaseName()).append("`").append("."); - } - sql.append("`").append(oldTable.getName()).append("`").append("\n"); - } - sql.append(" MODIFY COLUMN "); - HiveColumnTypeEnum typeEnum = HiveColumnTypeEnum.getByType(column.getColumnType()); - sql.append(typeEnum.buildColumn(column)); - sql.append(" "); - sql.append(" AFTER "); - sql.append(oldTable.getColumnList().get(max).getName()); - sql.append(";\n"); - n++; - if (Arrays.equals(newArray, targetArray)) { - return newArray; - } - String[] resultArray = buildSql(newArray, targetArray, sql, oldTable, newTable, n); - if (Arrays.equals(resultArray, targetArray)) { - return resultArray; - } - } - - - for (int i = 0; i < originalArray.length; i++) { - int a = findIndex(targetArray, originalArray[i]); - if (i != a && isMoveValid(originalArray, targetArray, i, a)) { - // oldTable.getColumnList中查找name为a - int finalI = i; - TableColumn column = oldTable.getColumnList().stream().filter(col -> StringUtils.equals(col.getName(), originalArray[finalI])).findFirst().get(); - if (n > 0) { - sql.append("ALTER TABLE "); - if (StringUtils.isNotBlank(oldTable.getDatabaseName())) { - sql.append("`").append(oldTable.getDatabaseName()).append("`").append("."); - } - sql.append("`").append(oldTable.getName()).append("`").append("\n"); - } - sql.append(" MODIFY COLUMN "); - HiveColumnTypeEnum typeEnum = HiveColumnTypeEnum.getByType(column.getColumnType()); - sql.append(typeEnum.buildColumn(column)); - sql.append(" "); - sql.append(" AFTER "); - if (i < a) { - sql.append(originalArray[a]); - } else { - sql.append(originalArray[a - 1]); - } - - sql.append(";\n"); - n++; - String[] newArray = moveElement(originalArray, i, a); - if (Arrays.equals(newArray, targetArray)) { - return newArray; - } - String[] resultArray = buildSql(newArray, targetArray, sql, oldTable, newTable, n); - if (Arrays.equals(resultArray, targetArray)) { - return resultArray; - } - } - } - return null; - } - - private static int findIndex(String[] array, String element) { - for (int i = 0; i < array.length; i++) { - if (array[i].equals(element)) { - return i; - } - } - return -1; - } - - private static boolean isMoveValid(String[] originalArray, String[] targetArray, int i, int a) { - System.out.println("i : " + i + " a:" + a); - return (i == 0 || a == 0 || !originalArray[i - 1].equals(targetArray[a - 1])) && - (i >= originalArray.length - 1 || a >= targetArray.length - 1 || !originalArray[i + 1].equals(targetArray[a + 1])); - } - - private static String[] moveElement(String[] originalArray, int from, int to) { - String[] newArray = new String[originalArray.length]; - System.arraycopy(originalArray, 0, newArray, 0, originalArray.length); - String temp = newArray[from]; - if (from < to) { - System.arraycopy(originalArray, from + 1, newArray, from, to - from); - } else { - System.arraycopy(originalArray, to, newArray, to + 1, from - to); - } - newArray[to] = temp; - System.out.println(ArrayUtil.toString(newArray)); - return newArray; - } - } diff --git a/chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/type/HiveColumnTypeEnum.java b/chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/type/HiveColumnTypeEnum.java index c5348b52b..2c19fd0ff 100644 --- a/chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/type/HiveColumnTypeEnum.java +++ b/chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/type/HiveColumnTypeEnum.java @@ -150,7 +150,7 @@ public String buildModifyColumn(TableColumn tableColumn) { if (!StringUtils.equalsIgnoreCase(tableColumn.getOldName(), tableColumn.getName())) { return StringUtils.join("CHANGE COLUMN `", tableColumn.getOldName(), "` ", buildCreateColumnSql(tableColumn)); } else { - return StringUtils.join("MODIFY COLUMN ", buildCreateColumnSql(tableColumn)); + return StringUtils.join("CHANGE `", tableColumn.getOldName(), "` ", buildCreateColumnSql(tableColumn)); } } return ""; diff --git a/chat2db-server/chat2db-plugins/chat2db-xugudb/pom.xml b/chat2db-server/chat2db-plugins/chat2db-xugudb/pom.xml new file mode 100644 index 000000000..f02c6049b --- /dev/null +++ b/chat2db-server/chat2db-plugins/chat2db-xugudb/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + + ai.chat2db + chat2db-plugins + ${revision} + ../pom.xml + + + + + ai.chat2db + chat2db-spi + + + + chat2db-xugudb + + + + src/main/java + + + **/*.json + + + + src/main/resources + + + + \ No newline at end of file diff --git a/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/XUGUDBManage.java b/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/XUGUDBManage.java new file mode 100644 index 000000000..024d7e91b --- /dev/null +++ b/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/XUGUDBManage.java @@ -0,0 +1,166 @@ +package ai.chat2db.plugin.xugudb; + +import ai.chat2db.spi.DBManage; +import ai.chat2db.spi.jdbc.DefaultDBManage; +import ai.chat2db.spi.sql.Chat2DBContext; +import ai.chat2db.spi.sql.ConnectInfo; +import ai.chat2db.spi.sql.SQLExecutor; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +public class XUGUDBManage extends DefaultDBManage implements DBManage { + private String format(String tableName) { + return "\"" + tableName + "\""; + } + + private static String ROUTINES_SQL + = "SELECT OWNER, NAME, TEXT FROM ALL_SOURCE WHERE TYPE = '%s' AND OWNER = '%s' AND NAME = '%s' ORDER BY LINE"; + private static String TRIGGER_SQL_LIST = "SELECT OWNER, TRIGGER_NAME FROM ALL_TRIGGERS WHERE OWNER = '%s'"; + + private static String TRIGGER_SQL + = "SELECT OWNER, TRIGGER_NAME, TABLE_OWNER, TABLE_NAME, TRIGGERING_TYPE, TRIGGERING_EVENT, STATUS, TRIGGER_BODY " + + "FROM ALL_TRIGGERS WHERE OWNER = '%s' AND TRIGGER_NAME = '%s'"; + + @Override + public String exportDatabase(Connection connection, String databaseName, String schemaName, boolean containData) throws SQLException { + StringBuilder sqlBuilder = new StringBuilder(); + exportTables(connection, sqlBuilder, schemaName, containData); + exportViews(connection, schemaName, sqlBuilder); + exportProcedures(connection, schemaName, sqlBuilder); + exportTriggers(connection, schemaName, sqlBuilder); + return sqlBuilder.toString(); + } + + private void exportTables(Connection connection, StringBuilder sqlBuilder, String schemaName, boolean containData) throws SQLException { + String sql = String.format("SELECT TABLE_NAME FROM ALL_TABLES where OWNER='%s' and TABLESPACE_NAME='MAIN'", schemaName); + try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) { + while (resultSet.next()) { + String tableName = resultSet.getString("TABLE_NAME"); + exportTable(connection, tableName, schemaName, sqlBuilder, containData); + } + } + } + + + private void exportTable(Connection connection, String tableName, String schemaName, StringBuilder sqlBuilder, boolean containData) throws SQLException { + String sql = """ + SELECT + (SELECT comments FROM user_tab_comments WHERE table_name = '%s') AS comments, + (SELECT dbms_metadata.get_ddl('TABLE', '%s', '%s') FROM dual) AS ddl + FROM dual; + """; + try (Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(String.format(sql, tableName, tableName, schemaName))) { + String formatSchemaName = format(schemaName); + String formatTableName = format(tableName); + if (resultSet.next()) { + sqlBuilder.append("DROP TABLE IF EXISTS ").append(formatSchemaName).append(".").append(formatTableName) + .append(";").append("\n") + .append(resultSet.getString("ddl")).append("\n"); + String comment = resultSet.getString("comments"); + if (StringUtils.isNotBlank(comment)) { + sqlBuilder.append("COMMENT ON TABLE ").append(formatSchemaName).append(".").append(formatTableName) + .append(" IS ").append("'").append(comment).append("';"); + } + exportTableColumnComment(connection, schemaName, tableName, sqlBuilder); + } + if (containData) { + exportTableData(connection, schemaName, tableName, sqlBuilder); + } + } + } + + private void exportTableColumnComment(Connection connection, String schemaName, String tableName, StringBuilder sqlBuilder) throws SQLException { + String sql = String.format("select COLNAME,COMMENT$ from SYS.SYSCOLUMNCOMMENTS\n" + + "where SCHNAME = '%s' and TVNAME = '%s'and TABLE_TYPE = 'TABLE';", schemaName, tableName); + try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) { + while (resultSet.next()) { + String columnName = resultSet.getString("COLNAME"); + String comment = resultSet.getString("COMMENT$"); + sqlBuilder.append("COMMENT ON COLUMN ").append(format(schemaName)).append(".").append(format(tableName)) + .append(".").append(format(columnName)).append(" IS ").append("'").append(comment).append("';").append("\n"); + } + } + } + + + private void exportViews(Connection connection, String schemaName, StringBuilder sqlBuilder) throws SQLException { + try (ResultSet resultSet = connection.getMetaData().getTables(null, schemaName, null, new String[]{"VIEW"})) { + while (resultSet.next()) { + String viewName = resultSet.getString("TABLE_NAME"); + exportView(connection, viewName, schemaName, sqlBuilder); + } + } + } + + private void exportView(Connection connection, String viewName, String schemaName, StringBuilder sqlBuilder) throws SQLException { + String sql = String.format("SELECT DBMS_METADATA.GET_DDL('VIEW','%s','%s') as ddl FROM DUAL;", viewName, schemaName); + try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) { + if (resultSet.next()) { + sqlBuilder.append(resultSet.getString("ddl")).append("\n"); + } + } + } + + private void exportProcedures(Connection connection, String schemaName, StringBuilder sqlBuilder) throws SQLException { + try (ResultSet resultSet = connection.getMetaData().getProcedures(null, schemaName, null)) { + while (resultSet.next()) { + String procedureName = resultSet.getString("PROCEDURE_NAME"); + exportProcedure(connection, schemaName, procedureName, sqlBuilder); + } + } + } + + private void exportProcedure(Connection connection, String schemaName, String procedureName, StringBuilder sqlBuilder) throws SQLException { + String sql = String.format(ROUTINES_SQL, "PROC", schemaName, procedureName); + try (Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(sql)) { + if (resultSet.next()) { + sqlBuilder.append(resultSet.getString("TEXT")).append("\n"); + } + } + } + + private void exportTriggers(Connection connection, String schemaName, StringBuilder sqlBuilder) throws SQLException { + String sql = String.format(TRIGGER_SQL_LIST, schemaName); + try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) { + while (resultSet.next()) { + String triggerName = resultSet.getString("TRIGGER_NAME"); + exportTrigger(connection, schemaName, triggerName, sqlBuilder); + } + } + } + + private void exportTrigger(Connection connection, String schemaName, String triggerName, StringBuilder sqlBuilder) throws SQLException { + String sql = String.format(TRIGGER_SQL, schemaName, triggerName); + try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) { + if (resultSet.next()) { + sqlBuilder.append(resultSet.getString("TRIGGER_BODY")).append("\n"); + } + } + } + + + @Override + public void connectDatabase(Connection connection, String database) { + ConnectInfo connectInfo = Chat2DBContext.getConnectInfo(); + if (ObjectUtils.anyNull(connectInfo) || StringUtils.isEmpty(connectInfo.getSchemaName())) { + return; + } + String schemaName = connectInfo.getSchemaName(); + try { + SQLExecutor.getInstance().execute(connection, "SET SCHEMA TO " + schemaName); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + @Override + public void dropTable(Connection connection, String databaseName, String schemaName, String tableName) { + String sql = "DROP TABLE IF EXISTS " + tableName; + SQLExecutor.getInstance().execute(connection, sql, resultSet -> null); + } +} diff --git a/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/XUGUDBMetaData.java b/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/XUGUDBMetaData.java new file mode 100644 index 000000000..d7d3f74c8 --- /dev/null +++ b/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/XUGUDBMetaData.java @@ -0,0 +1,355 @@ +package ai.chat2db.plugin.xugudb; + +import ai.chat2db.plugin.xugudb.builder.XUGUDBSqlBuilder; +import ai.chat2db.plugin.xugudb.type.XUGUDBColumnTypeEnum; +import ai.chat2db.plugin.xugudb.type.XUGUDBDefaultValueEnum; +import ai.chat2db.plugin.xugudb.type.XUGUDBIndexTypeEnum; +import ai.chat2db.spi.MetaData; +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; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.stream.Collectors; + +import static ai.chat2db.spi.util.SortUtils.sortDatabase; + +public class XUGUDBMetaData extends DefaultMetaService implements MetaData { + private List systemDatabases = Arrays.asList("information_schema", "performance_schema", "sys"); + + @Override + public List databases(Connection connection) { + List databases = SQLExecutor.getInstance().databases(connection); + return sortDatabase(databases, systemDatabases, connection); + } + + private List systemSchemas = Arrays.asList("CTISYS", "SYS", "SYSDBA", "SYSSSO", "SYSAUDITOR"); + + @Override + public List schemas(Connection connection, String databaseName) { + List schemas = SQLExecutor.getInstance().schemas(connection, databaseName, null); + return SortUtils.sortSchema(schemas, systemSchemas); + } + + private String format(String tableName) { + return "\"" + tableName + "\""; + } + + @Override + public String tableDDL(Connection connection, String databaseName, String schemaName, String tableName) { + String sql = """ + SELECT + (SELECT dbms_metadata.get_ddl('%s.%s') FROM dual) AS ddl + FROM dual; + """; + StringBuilder ddlBuilder = new StringBuilder(); + String tableDDLSql = String.format(sql, schemaName, tableName); + SQLExecutor.getInstance().execute(connection, tableDDLSql, resultSet -> { + if (resultSet.next()) { + String ddl = resultSet.getString("ddl"); + ddlBuilder.append(ddl).append("\n"); + } + }); + return ddlBuilder.toString(); + } + + private static String FUNCTIONS_SQL + = "select p.PROC_NAME From ALL_PROCEDURES p LEFT JOIN DBA_DATABASES DB ON p.DB_ID = DB.DB_ID LEFT JOIN DBA_SCHEMAS CH ON p.SCHEMA_ID = CH.SCHEMA_ID where DB.DB_NAME = '%s' and CH.SCHEMA_NAME = '%s' and p.RET_TYPE is not null "; + + + @Override + public List functions(Connection connection, String databaseName, String schemaName) { + List functions = new ArrayList<>(); + String sql = String.format(FUNCTIONS_SQL, databaseName, schemaName); + return SQLExecutor.getInstance().execute(connection, sql, resultSet -> { + while (resultSet.next()) { + Function function = new Function(); + function.setDatabaseName(databaseName); + function.setSchemaName(schemaName); + function.setFunctionName(resultSet.getString("PROC_NAME")); + functions.add(function); + } + return functions; + }); + } + + private static String ROUTINES_SQL + = "select DB.DB_NAME, CH.SCHEMA_NAME, p.PROC_NAME, p.DEFINE From ALL_PROCEDURES p LEFT JOIN DBA_DATABASES DB ON p.DB_ID = DB.DB_ID LEFT JOIN DBA_SCHEMAS CH ON p.SCHEMA_ID = CH.SCHEMA_ID where DB.DB_NAME = '%s' and CH.SCHEMA_NAME = '%s' and p.PROC_NAME = '%s' and p.RET_TYPE is not null "; + + @Override + public Function function(Connection connection, @NotEmpty String databaseName, String schemaName, + String functionName) { + + String sql = String.format(ROUTINES_SQL, databaseName, schemaName, functionName); + return SQLExecutor.getInstance().execute(connection, sql, resultSet -> { + StringBuilder sb = new StringBuilder(); + while (resultSet.next()) { + sb.append(resultSet.getString("DEFINE") + "\n"); + } + Function function = new Function(); + function.setDatabaseName(databaseName); + function.setSchemaName(schemaName); + function.setFunctionName(functionName); + function.setFunctionBody(sb.toString()); + return function; + + }); + + } + + private static String PROCEDURE_SQL + = "select DB.DB_NAME, CH.SCHEMA_NAME, p.PROC_NAME, p.DEFINE From ALL_PROCEDURES p LEFT JOIN DBA_DATABASES DB ON p.DB_ID = DB.DB_ID LEFT JOIN DBA_SCHEMAS CH ON p.SCHEMA_ID = CH.SCHEMA_ID where DB.DB_NAME = '%s' and CH.SCHEMA_NAME = '%s' and p.PROC_NAME = '%s' and p.RET_TYPE is null "; + + @Override + public Procedure procedure(Connection connection, @NotEmpty String databaseName, String schemaName, + String procedureName) { + String sql = String.format(PROCEDURE_SQL, databaseName, schemaName, procedureName); + return SQLExecutor.getInstance().execute(connection, sql, resultSet -> { + StringBuilder sb = new StringBuilder(); + while (resultSet.next()) { + sb.append(resultSet.getString("DEFINE") + "\n"); + } + Procedure procedure = new Procedure(); + procedure.setDatabaseName(databaseName); + procedure.setSchemaName(schemaName); + procedure.setProcedureName(procedureName); + procedure.setProcedureBody(sb.toString()); + return procedure; + }); + } + + private static String PROCEDURES_SQL + = "select p.PROC_NAME From ALL_PROCEDURES p LEFT JOIN DBA_DATABASES DB ON p.DB_ID = DB.DB_ID LEFT JOIN DBA_SCHEMAS CH ON p.SCHEMA_ID = CH.SCHEMA_ID where DB.DB_NAME = '%s' and CH.SCHEMA_NAME = '%s' and p.RET_TYPE is null "; + + + @Override + public List procedures(Connection connection, String databaseName, String schemaName) { + List procedures = new ArrayList<>(); + String sql = String.format(PROCEDURES_SQL, databaseName, schemaName); + return SQLExecutor.getInstance().execute(connection, sql, resultSet -> { + while (resultSet.next()) { + Procedure procedure = new Procedure(); + procedure.setDatabaseName(databaseName); + procedure.setSchemaName(schemaName); + procedure.setProcedureName(resultSet.getString("PROC_NAME")); + procedures.add(procedure); + } + return procedures; + }); + } + + + private static String TRIGGER_SQL + = "select t.trig_name,t.DEFINE from all_triggers t LEFT JOIN DBA_DATABASES DB ON t.DB_ID = DB.DB_ID LEFT JOIN DBA_SCHEMAS CH ON t.SCHEMA_ID = CH.SCHEMA_ID where DB.DB_NAME = '%s' and CH.SCHEMA_NAME = '%s' and t.trig_name = '%s'"; + + private static String TRIGGER_SQL_LIST = "select t.trig_name from all_triggers t LEFT JOIN DBA_DATABASES DB ON t.DB_ID = DB.DB_ID LEFT JOIN DBA_SCHEMAS CH ON t.SCHEMA_ID = CH.SCHEMA_ID where DB.DB_NAME = '%s' and CH.SCHEMA_NAME = '%s'"; + + @Override + public List triggers(Connection connection, String databaseName, String schemaName) { + List triggers = new ArrayList<>(); + String sql = String.format(TRIGGER_SQL_LIST, databaseName, schemaName); + return SQLExecutor.getInstance().execute(connection, sql, resultSet -> { + while (resultSet.next()) { + Trigger trigger = new Trigger(); + trigger.setTriggerName(resultSet.getString("trig_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, schemaName, 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("DEFINE")); + } + return trigger; + }); + } + + private static String VIEW_SQL_LIST = "SELECT DB.DB_NAME, CH.SCHEMA_NAME, v.VIEW_NAME, v.DEFINE, v.OPTION,v.VALID, v.IS_SYS, v.COMMENTS FROM all_views v LEFT JOIN DBA_DATABASES DB ON v.DB_ID = DB.DB_ID LEFT JOIN DBA_SCHEMAS CH ON v.SCHEMA_ID = CH.SCHEMA_ID where DB.DB_NAME = '%s' and CH.SCHEMA_NAME = '%s'"; + + @Override + public List views(Connection connection, String databaseName, String schemaName) { + String sql = String.format(VIEW_SQL_LIST, databaseName, schemaName); + List
tables = new ArrayList<>(); + return SQLExecutor.getInstance().execute(connection, sql, resultSet -> { + Table table = new Table(); + table.setDatabaseName(databaseName); + table.setSchemaName(schemaName); + while (resultSet.next()) { + table.setName(resultSet.getString("VIEW_NAME")); + table.setDdl(resultSet.getString("DEFINE")); + tables.add(table); + } + return tables; + }); + + } + + private static String VIEW_SQL + = " SELECT CH.SCHEMA_NAME, v.VIEW_NAME, v.DEFINE FROM ALL_VIEWS v LEFT JOIN DBA_DATABASES DB ON v.DB_ID = DB.DB_ID LEFT JOIN DBA_SCHEMAS CH ON v.SCHEMA_ID = CH.SCHEMA_ID where DB.DB_NAME = '%s' and CH.SCHEMA_NAME = '%s' and v.VIEW_NAME = '%s'"; + + @Override + public Table view(Connection connection, String databaseName, String schemaName, String viewName) { + String sql = String.format(VIEW_SQL, databaseName, schemaName, 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("DEFINE")); + } + return table; + }); + } + + private static String INDEX_SQL = "SELECT i.INDEX_NAME, CASE i.INDEX_TYPE WHEN 0 THEN 'BTREE' WHEN 1 THEN 'RTREE' WHEN 2 THEN 'FULLTEXT' WHEN 3 THEN 'BITMAP' WHEN 4 THEN 'UNION' END AS INDEX_TYPE, i.IS_PRIMARY," + + " i.IS_UNIQUE, i.FIELD_NUM, REPLACE (KEYS, '\"', '') AS KEYS FROM ALL_INDEXES i LEFT JOIN ALL_TABLES T ON i.TABLE_ID = T.TABLE_ID LEFT JOIN ALL_SCHEMAS CH ON CH.USER_ID = T.USER_ID AND CH.DB_ID = i.DB_ID" + + " where CH.SCHEMA_NAME = '%s' and T.TABLE_NAME = '%s'"; + + @Override + public List indexes(Connection connection, String databaseName, String schemaName, String tableName) { + String sql = String.format(INDEX_SQL, schemaName, tableName); + return SQLExecutor.getInstance().execute(connection, sql, resultSet -> { + LinkedHashMap map = new LinkedHashMap(); + while (resultSet.next()) { + String keyName = resultSet.getString("INDEX_NAME"); + TableIndex tableIndex = map.get(keyName); + if (tableIndex != null) { + tableIndex.setColumnList(getTableIndexColumn(resultSet)); + } else { + TableIndex index = new TableIndex(); + index.setDatabaseName(databaseName); + index.setSchemaName(schemaName); + index.setTableName(tableName); + index.setName(keyName); + index.setUnique((resultSet.getBoolean("IS_UNIQUE"))); +// index.setType(resultSet.getString("Index_type")); +// index.setComment(resultSet.getString("Index_comment")); + index.setColumnList(getTableIndexColumn(resultSet)); + if (resultSet.getBoolean("IS_PRIMARY")) { + index.setType(XUGUDBIndexTypeEnum.PRIMARY_KEY.getName()); + } else if (index.getUnique()) { + index.setType(XUGUDBIndexTypeEnum.UNIQUE.getName()); + } else if ("BTREE".equalsIgnoreCase(resultSet.getString("INDEX_TYPE"))) { + index.setType(XUGUDBIndexTypeEnum.BTREE.getName()); + } else { + index.setType(XUGUDBIndexTypeEnum.NORMAL.getName()); + } + map.put(keyName, index); + } + } + return map.values().stream().collect(Collectors.toList()); + }); + + } + + private List getTableIndexColumn(ResultSet resultSet) throws SQLException { + List tableIndexColumnList = new ArrayList<>(); + String[] keys = resultSet.getString("KEYS").split(","); + for (String key : keys) { + TableIndexColumn tableIndexColumn = new TableIndexColumn(); + tableIndexColumn.setColumnName(key); + // TODO 先临时赋值0 + tableIndexColumn.setOrdinalPosition((short) 0); + tableIndexColumnList.add(tableIndexColumn); + } + return tableIndexColumnList; + } + + + private static String SELECT_TABLE_COLUMNS = "select c.* from ALL_COLUMNS c LEFT JOIN ALL_TABLES T ON c.TABLE_ID = T.TABLE_ID LEFT JOIN ALL_SCHEMAS CH ON CH.USER_ID = T.USER_ID AND CH.DB_ID = c.DB_ID where CH.SCHEMA_NAME = '%s' and T.TABLE_NAME = '%s' order by c.COL_NO"; + + @Override + public List columns(Connection connection, String databaseName, String schemaName, String tableName) { + String sql = String.format(SELECT_TABLE_COLUMNS, schemaName, tableName); + List tableColumns = new ArrayList<>(); + return SQLExecutor.getInstance().execute(connection, sql, resultSet -> { + while (resultSet.next()) { + TableColumn column = new TableColumn(); + column.setDatabaseName(databaseName); + column.setTableName(tableName); + column.setOldName(resultSet.getString("COL_NAME")); + column.setName(resultSet.getString("COL_NAME")); + //column.setColumnType(resultSet.getString("COLUMN_TYPE")); + if (resultSet.getBoolean("VARYING")) { + if (resultSet.getString("TYPE_NAME").toUpperCase().equals(XUGUDBColumnTypeEnum.CHAR.name())) { + column.setColumnType("VAR" + resultSet.getString("TYPE_NAME").toUpperCase()); + } else { + column.setColumnType(resultSet.getString("TYPE_NAME").toUpperCase()); + } + } else { + column.setColumnType(resultSet.getString("TYPE_NAME").toUpperCase()); + } + + //column.setDataType(resultSet.getInt("DATA_TYPE")); + column.setDefaultValue(resultSet.getString("DEF_VAL")); + //column.setAutoIncrement(resultSet.getString("EXTRA").contains("auto_increment")); + column.setComment(resultSet.getString("COMMENTS")); + // column.setPrimaryKey("PRI".equalsIgnoreCase(resultSet.getString("COLUMN_KEY"))); + column.setNullable(resultSet.getBoolean("NOT_NULL") ? 0 : 1); + column.setOrdinalPosition(resultSet.getInt("SCALE")); + // column.setDecimalDigits(resultSet.getInt("NUMERIC_SCALE")); + //column.setCharSetName(resultSet.getString("CHARACTER_SET_NAME")); + // column.setCollationName(resultSet.getString("COLLATION_NAME")); + column.setColumnSize(resultSet.getInt("SCALE") == -1 ? null : resultSet.getInt("SCALE")); + tableColumns.add(column); + } + return tableColumns; + }); + } + + @Override + public SqlBuilder getSqlBuilder() { + return new XUGUDBSqlBuilder(); + } + + @Override + public TableMeta getTableMeta(String databaseName, String schemaName, String tableName) { + return TableMeta.builder() + .columnTypes(XUGUDBColumnTypeEnum.getTypes()) + .charsets(Lists.newArrayList()) + .collations(Lists.newArrayList()) + .indexTypes(XUGUDBIndexTypeEnum.getIndexTypes()) + .defaultValues(XUGUDBDefaultValueEnum.getDefaultValues()) + .build(); + } + + @Override + public String getMetaDataName(String... names) { + if (Arrays.stream(names).count() > 1) { + return Arrays.stream(names).skip(1).filter(name -> StringUtils.isNotBlank(name)).map(name -> "\"" + name + "\"").collect(Collectors.joining(".")); + } + return Arrays.stream(names).filter(name -> StringUtils.isNotBlank(name)).map(name -> "\"" + name + "\"").collect(Collectors.joining(".")); + } + + + @Override + public List getSystemSchemas() { + return systemSchemas; + } + +} diff --git a/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/XUGUDBPlugin.java b/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/XUGUDBPlugin.java new file mode 100644 index 000000000..d53807728 --- /dev/null +++ b/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/XUGUDBPlugin.java @@ -0,0 +1,25 @@ +package ai.chat2db.plugin.xugudb; + +import ai.chat2db.spi.DBManage; +import ai.chat2db.spi.MetaData; +import ai.chat2db.spi.Plugin; +import ai.chat2db.spi.config.DBConfig; +import ai.chat2db.spi.util.FileUtils; + +public class XUGUDBPlugin implements Plugin { + @Override + public DBConfig getDBConfig() { + return FileUtils.readJsonValue(this.getClass(),"xugudb.json", DBConfig.class); + + } + + @Override + public MetaData getMetaData() { + return new XUGUDBMetaData(); + } + + @Override + public DBManage getDBManage() { + return new XUGUDBManage(); + } +} diff --git a/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/builder/XUGUDBSqlBuilder.java b/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/builder/XUGUDBSqlBuilder.java new file mode 100644 index 000000000..4b808b977 --- /dev/null +++ b/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/builder/XUGUDBSqlBuilder.java @@ -0,0 +1,137 @@ +package ai.chat2db.plugin.xugudb.builder; + +import ai.chat2db.plugin.xugudb.type.XUGUDBColumnTypeEnum; +import ai.chat2db.plugin.xugudb.type.XUGUDBIndexTypeEnum; +import ai.chat2db.spi.enums.EditStatus; +import ai.chat2db.spi.jdbc.DefaultSqlBuilder; +import ai.chat2db.spi.model.Schema; +import ai.chat2db.spi.model.Table; +import ai.chat2db.spi.model.TableColumn; +import ai.chat2db.spi.model.TableIndex; +import org.apache.commons.lang3.StringUtils; + +import java.util.Objects; + +public class XUGUDBSqlBuilder extends DefaultSqlBuilder { + + + @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; + } + XUGUDBColumnTypeEnum typeEnum = XUGUDBColumnTypeEnum.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; + } + XUGUDBIndexTypeEnum indexTypeEnum = XUGUDBIndexTypeEnum.getByType(tableIndex.getType()); + script.append("\n").append("").append(indexTypeEnum.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(oldTable.getName()).append("\""); + script.append(" ").append("RENAME TO ").append("\"").append(newTable.getName()).append("\"").append(";\n"); + } + if (!StringUtils.equalsIgnoreCase(oldTable.getComment(), newTable.getComment())) { + script.append("").append(buildTableComment(newTable)).append(";\n"); + } + + + // append modify column + for (TableColumn tableColumn : newTable.getColumnList()) { + String editStatus = tableColumn.getEditStatus(); + if (StringUtils.isNotBlank(editStatus)) { + XUGUDBColumnTypeEnum typeEnum = XUGUDBColumnTypeEnum.getByType(tableColumn.getColumnType()); + script.append("\t").append(typeEnum.buildModifyColumn(tableColumn)).append(";\n"); + if (StringUtils.isNotBlank(tableColumn.getComment())&&!Objects.equals(EditStatus.DELETE.toString(),editStatus)) { + script.append("\n").append(buildComment(tableColumn)).append(";\n"); + } + } + } + + // append modify index + for (TableIndex tableIndex : newTable.getIndexList()) { + if (StringUtils.isNotBlank(tableIndex.getEditStatus()) && StringUtils.isNotBlank(tableIndex.getType())) { + XUGUDBIndexTypeEnum mysqlIndexTypeEnum = XUGUDBIndexTypeEnum.getByType(tableIndex.getType()); + script.append("\t").append(mysqlIndexTypeEnum.buildModifyIndex(tableIndex)).append(";\n"); + } + } + if (script.length() > 2) { + script = new StringBuilder(script.substring(0, script.length() - 2)); + script.append(";"); + } + + 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(); + } + + @Override + public String buildCreateSchemaSql(Schema schema) { + StringBuilder sqlBuilder = new StringBuilder(); + sqlBuilder.append("CREATE SCHEMA \""+schema.getName()+"\""); + if(StringUtils.isNotBlank(schema.getOwner())){ + sqlBuilder.append(" AUTHORIZATION ").append(schema.getOwner()); + } + + return sqlBuilder.toString(); + } +} diff --git a/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/type/XUGUDBColumnTypeEnum.java b/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/type/XUGUDBColumnTypeEnum.java new file mode 100644 index 000000000..405a9f439 --- /dev/null +++ b/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/type/XUGUDBColumnTypeEnum.java @@ -0,0 +1,304 @@ +package ai.chat2db.plugin.xugudb.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.*; + +public enum XUGUDBColumnTypeEnum implements ColumnBuilder { + + TINYINT("TINYINT", false, false, true, false, false, false, true, true, false, false), + + BIGINT("BIGINT", false, false, true, false, false, false, true, true, false, false), + + SMALLINT("SMALLINT", false, false, true, false, false, false, true, true, false, false), + + INTEGER("INTEGER", false, false, true, false, false, false, true, true, false, false), + + NUMERIC("NUMERIC", true, true, true, false, false, false, true, true, false, false), + + FLOAT("FLOAT", true, false, true, false, false, false, true, true, false, false), + + DOUBLE("DOUBLE", false, false, true, false, false, false, true, true, false, false), + + CHAR("CHAR", true, false, true, false, false, false, true, true, false, true), + + NCHAR("NCHAR", true, false, true, false, false, false, true, true, false, true), + + VARCHAR("VARCHAR", true, false, true, false, false, false, true, true, false, true), + + VARCHAR2("VARCHAR2", true, false, true, false, false, false, true, true, false, true), + + DATE("DATE", false, false, true, false, false, false, true, true, false, false), + + TIME("TIME", false, false, true, false, false, false, true, true, false, false), + + TIME_WITH_TIME_ZONE("TIME WITH TIME ZONE", false, false, true, false, false, false, true, true, false, false), + + DATETIME("DATETIME", false, false, true, false, false, false, true, true, false, false), + + DATETIME_WITH_TIME_ZONE("DATETIME WITH TIME ZONE", false, false, true, false, false, false, true, true, false, false), + + TIMESTAMP("TIMESTAMP", false, false, true, false, false, false, true, true, false, false), + + TIMESTAMP_WITH_TIME_ZONE("TIMESTAMP WITH TIME ZONE", false, false, true, false, false, false, true, true, false, false), + + INTERVAL_YEAR("INTERVAL YEAR", false, false, true, false, false, false, true, true, false, false), + + INTERVAL_MONTH("INTERVAL MONTH", false, false, true, false, false, false, true, true, false, false), + + INTERVAL_DAY("INTERVAL DAY", false, false, true, false, false, false, true, true, false, false), + + INTERVAL_HOUR("INTERVAL HOUR", false, false, true, false, false, false, true, true, false, false), + + INTERVAL_MINUTE("INTERVAL MINUTE", false, false, true, false, false, false, true, true, false, false), + + INTERVAL_SECOND("INTERVAL SECOND", false, false, true, false, false, false, true, true, false, false), + + INTERVAL_YEAR_TO_MONTH("INTERVAL YEAR TO MONTH", true, false, true, false, false, false, true, true, false, false), + + INTERVAL_DAY_TO_HOUR("INTERVAL DAY TO HOUR", true, false, true, false, false, false, true, true, false, false), + + INTERVAL_DAY_TO_MINUTE("INTERVAL DAY TO MINUTE", true, false, true, false, false, false, true, true, false, false), + + INTERVAL_DAY_TO_SECOND("INTERVAL DAY TO SECOND", true, false, true, false, false, false, true, true, false, false), + + INTERVAL_HOUR_TO_MINUTE("INTERVAL HOUR TO MINUTE", true, false, true, false, false, false, true, true, false, false), + + INTERVAL_HOUR_TO_SECOND("INTERVAL HOUR TO SECOND", true, false, true, false, false, false, true, true, false, false), + + INTERVAL_MINUTE_TO_SECOND("INTERVAL MINUTE TO SECOND", true, false, true, false, false, false, true, true, false, false), + + BLOB("BLOB", false, false, true, false, false, false, true, true, false, false), + + CLOB("CLOB", false, false, true, false, false, false, true, true, false, false), + + BOOLEAN("BOOLEAN", false, false, true, false, false, false, true, true, false, false), + + BINARY("BINARY", false, false, true, false, false, false, true, true, false, false), + + OBJECT("OBJECT", false, false, true, false, false, false, true, true, false, false), + + VARRAY("VARRAY", false, false, true, false, false, false, true, true, false, false), + ; + private ColumnType columnType; + + public static XUGUDBColumnTypeEnum getByType(String dataType) { + return COLUMN_TYPE_MAP.get(dataType.toUpperCase()); + } + + private static Map COLUMN_TYPE_MAP = Maps.newHashMap(); + + static { + for (XUGUDBColumnTypeEnum value : XUGUDBColumnTypeEnum.values()) { + COLUMN_TYPE_MAP.put(value.getColumnType().getTypeName(), value); + } + } + + public ColumnType getColumnType() { + return columnType; + } + + + XUGUDBColumnTypeEnum(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) { + XUGUDBColumnTypeEnum 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(buildAutoIncrement(column, type)).append(" "); + + script.append(buildNullable(column, type)).append(" "); + + return script.toString(); + } + + public String buildUpdateColumnSql(TableColumn column) { + XUGUDBColumnTypeEnum type = COLUMN_TYPE_MAP.get(column.getColumnType().toUpperCase()); + if (type == null) { + return ""; + } + StringBuilder script = new StringBuilder(); + script.append("ALTER TABLE ").append("\"").append(column.getSchemaName()).append("\".\"").append(column.getTableName()).append("\""); + script.append(" ").append("MODIFY (").append("\"").append(column.getName()).append("\"").append(" "); + boolean isModify = false; + Integer oldColumnSize = Optional.ofNullable(column.getOldColumn()) + .map(TableColumn::getColumnSize) + .orElse(null); + + Integer newColumnSize = Optional.ofNullable(column.getColumnSize()) + .orElse(null); + if (!column.getOldColumn().getColumnType().equals(column.getColumnType()) + || !Objects.equals(oldColumnSize, newColumnSize)) { + script.append(buildDataType(column, type)).append(" "); + isModify = true; + } + if (!Objects.equals(column.getOldColumn().getNullable(), column.getNullable())) { + script.append(buildNullable(column, type)).append(" "); + isModify = true; + } + script.append(") \n"); + + return isModify ? script.toString() : ""; + } + + private String buildAutoIncrement(TableColumn column, XUGUDBColumnTypeEnum type) { + if (!type.getColumnType().isSupportAutoIncrement()) { + return ""; + } + if (column.getAutoIncrement() != null && column.getAutoIncrement() + && column.getSeed() != null && column.getSeed() > 0 && column.getIncrement() != null && column.getIncrement() > 0) { + return "IDENTITY(" + column.getSeed() + "," + column.getIncrement() + ")"; + } + if (column.getAutoIncrement() != null && column.getAutoIncrement()) { + return "IDENTITY(1,1)"; + } + return ""; + } + + private String buildNullable(TableColumn column, XUGUDBColumnTypeEnum type) { + if (!type.getColumnType().isSupportNullable()) { + return ""; + } + if (column.getNullable() != null && 1 == column.getNullable()) { + if (EditStatus.MODIFY.name().equals(column.getEditStatus())) { + return "DROP NOT NULL"; + } else { + return "NULL"; + } + } else { + if (EditStatus.MODIFY.name().equals(column.getEditStatus())) { + return "SET NOT NULL"; + } else { + return "NOT NULL"; + } + } + } + + private String buildDefaultValue(TableColumn column, XUGUDBColumnTypeEnum 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, XUGUDBColumnTypeEnum type) { + String columnType = type.columnType.getTypeName(); + if (Arrays.asList(CHAR, VARCHAR, VARCHAR2).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(FLOAT, 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).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(); + } + + if (Arrays.asList(INTERVAL_DAY_TO_HOUR, + INTERVAL_DAY_TO_MINUTE, INTERVAL_DAY_TO_SECOND, + INTERVAL_HOUR_TO_MINUTE, + INTERVAL_HOUR_TO_SECOND, + INTERVAL_MINUTE_TO_SECOND, + INTERVAL_YEAR_TO_MONTH).contains(type)) { + StringBuilder script = new StringBuilder(); + if (column.getColumnSize() == null) { + script.append(columnType); + } else { + String[] split = columnType.split(" "); + if (split.length == 4) { + script.append(split[0]).append(" ").append(split[1]).append(" (").append(column.getColumnSize()).append(") ").append(split[2]).append(" ").append(split[3]); + } + } + 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(tableColumn.getTableName()).append("\""); + script.append(" ").append("DROP COLUMN ").append("\"").append(tableColumn.getName()).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(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(); + if (!StringUtils.equalsIgnoreCase(tableColumn.getOldName(), tableColumn.getName())) { + script.append("ALTER TABLE ").append("\"").append(tableColumn.getSchemaName()).append("\".\"").append(tableColumn.getTableName()).append("\""); + script.append(" ").append("RENAME COLUMN ").append("\"").append(tableColumn.getOldName()).append("\"").append(" TO ").append("\"").append(tableColumn.getName()).append("\" ").append(";\n").append(buildUpdateColumnSql(tableColumn)); + //script.append(";\n"); + } else { + //script.append("ALTER TABLE ").append("\"").append(tableColumn.getSchemaName()).append("\".\"").append(tableColumn.getTableName()).append("\""); + script.append(buildUpdateColumnSql(tableColumn)).append("\n"); + } + + return script.toString(); + + } + return ""; + } + + public static List getTypes() { + return Arrays.stream(XUGUDBColumnTypeEnum.values()).map(columnTypeEnum -> + columnTypeEnum.getColumnType() + ).toList(); + } +} diff --git a/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/type/XUGUDBDefaultValueEnum.java b/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/type/XUGUDBDefaultValueEnum.java new file mode 100644 index 000000000..e9833922d --- /dev/null +++ b/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/type/XUGUDBDefaultValueEnum.java @@ -0,0 +1,27 @@ +package ai.chat2db.plugin.xugudb.type; + +import ai.chat2db.spi.model.DefaultValue; + +import java.util.Arrays; +import java.util.List; + +public enum XUGUDBDefaultValueEnum { + EMPTY_STRING("EMPTY_STRING"), + NULL("NULL"), + ; + private DefaultValue defaultValue; + + XUGUDBDefaultValueEnum(String defaultValue) { + this.defaultValue = new DefaultValue(defaultValue); + } + + + public DefaultValue getDefaultValue() { + return defaultValue; + } + + public static List getDefaultValues() { + return Arrays.stream(XUGUDBDefaultValueEnum.values()).map(XUGUDBDefaultValueEnum::getDefaultValue).collect(java.util.stream.Collectors.toList()); + } + +} diff --git a/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/type/XUGUDBIndexTypeEnum.java b/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/type/XUGUDBIndexTypeEnum.java new file mode 100644 index 000000000..e57bf1e32 --- /dev/null +++ b/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/type/XUGUDBIndexTypeEnum.java @@ -0,0 +1,129 @@ +package ai.chat2db.plugin.xugudb.type; + +import ai.chat2db.spi.enums.EditStatus; +import ai.chat2db.spi.model.IndexType; +import ai.chat2db.spi.model.TableIndex; +import ai.chat2db.spi.model.TableIndexColumn; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; +import java.util.List; + +public enum XUGUDBIndexTypeEnum { + + PRIMARY_KEY("Primary", "PRIMARY KEY"), + + NORMAL("Normal", "INDEX"), + + UNIQUE("Unique", "UNIQUE INDEX"), + + BTREE("BTREE", "btree"); + + + + public IndexType getIndexType() { + return indexType; + } + + public void setIndexType(IndexType indexType) { + this.indexType = indexType; + } + + private IndexType indexType; + + + public String getName() { + return name; + } + + private String name; + + + public String getKeyword() { + return keyword; + } + + private String keyword; + + XUGUDBIndexTypeEnum(String name, String keyword) { + this.name = name; + this.keyword = keyword; + this.indexType = new IndexType(name); + } + + + public static XUGUDBIndexTypeEnum getByType(String type) { + for (XUGUDBIndexTypeEnum value : XUGUDBIndexTypeEnum.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("ALTER TABLE \"").append(tableIndex.getSchemaName()).append("\".\"").append(tableIndex.getTableName()).append("\" ADD PRIMARY KEY ").append(buildIndexColumn(tableIndex)); + } else { + if (UNIQUE.equals(this)) { + script.append("CREATE UNIQUE INDEX "); + } else { + script.append("CREATE INDEX "); + } + script.append(buildIndexName(tableIndex)).append(" ON \"").append(tableIndex.getSchemaName()).append("\".\"").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()) && !PRIMARY_KEY.equals(this)) { + 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 (XUGUDBIndexTypeEnum.PRIMARY_KEY.getName().equals(tableIndex.getType())) { + String tableName = "\"" + tableIndex.getSchemaName() + "\"." + "\"" + tableIndex.getTableName() + "\""; + return StringUtils.join("ALTER TABLE ",tableName," DROP PRIMARY KEY"); + } + StringBuilder script = new StringBuilder(); + script.append("DROP INDEX "); + script.append(buildIndexName(tableIndex)); + + return script.toString(); + } + + public static List getIndexTypes() { + return Arrays.asList(XUGUDBIndexTypeEnum.values()).stream().map(XUGUDBIndexTypeEnum::getIndexType).collect(java.util.stream.Collectors.toList()); + } +} diff --git a/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/xugudb.json b/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/xugudb.json new file mode 100644 index 000000000..b69036f09 --- /dev/null +++ b/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/java/ai/chat2db/plugin/xugudb/xugudb.json @@ -0,0 +1,18 @@ +{ + "dbType": "XUGUDB", + "supportDatabase": true, + "supportSchema": true, + "driverConfigList": [ + { + "url": "jdbc:xugu://localhost:5138/", + "defaultDriver": true, + "custom": false, + "downloadJdbcDriverUrls": [ + "https://cdn.chat2db-ai.com/lib/xugu-jdbc-12.1.12.jar" + ], + "jdbcDriver": "xugu-jdbc-12.1.12.jar", + "jdbcDriverClass": "com.xugu.cloudjdbc.Driver" + } + ], + "name": "XUGUDB" +} diff --git a/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin b/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin new file mode 100644 index 000000000..1fbe331d5 --- /dev/null +++ b/chat2db-server/chat2db-plugins/chat2db-xugudb/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin @@ -0,0 +1 @@ +ai.chat2db.plugin.xugudb.XUGUDBPlugin \ No newline at end of file diff --git a/chat2db-server/chat2db-plugins/pom.xml b/chat2db-server/chat2db-plugins/pom.xml index 17be8e0e8..f0c4babb9 100644 --- a/chat2db-server/chat2db-plugins/pom.xml +++ b/chat2db-server/chat2db-plugins/pom.xml @@ -29,6 +29,7 @@ chat2db-presto chat2db-hive chat2db-kingbase + chat2db-xugudb \ No newline at end of file diff --git a/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/pom.xml b/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/pom.xml index 47a86c68a..64fc263f2 100644 --- a/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/pom.xml +++ b/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/pom.xml @@ -121,5 +121,10 @@ chat2db-sqlserver ${revision} + + ai.chat2db + chat2db-xugudb + ${revision} +