support clickhouse create/update

This commit is contained in:
heshujun
2024-02-25 16:55:21 +08:00
parent 25405399b0
commit fe3ad6673e
10 changed files with 907 additions and 15 deletions

View File

@ -1,8 +1,293 @@
package ai.chat2db.plugin.clickhouse;
import ai.chat2db.plugin.clickhouse.builder.ClickHouseSqlBuilder;
import ai.chat2db.plugin.clickhouse.type.ClickHouseColumnTypeEnum;
import ai.chat2db.plugin.clickhouse.type.ClickHouseEngineTypeEnum;
import ai.chat2db.plugin.clickhouse.type.ClickHouseIndexTypeEnum;
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 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 ClickHouseMetaData extends DefaultMetaService implements MetaData {
private static String ROUTINES_SQL
=
"SELECT SPECIFIC_NAME, ROUTINE_COMMENT, ROUTINE_DEFINITION FROM information_schema.routines WHERE "
+ "routine_type = '%s' AND ROUTINE_SCHEMA ='%s' AND "
+ "routine_name = '%s';";
private static String TRIGGER_SQL
= "SELECT TRIGGER_NAME,EVENT_MANIPULATION, ACTION_STATEMENT FROM INFORMATION_SCHEMA.TRIGGERS where "
+ "TRIGGER_SCHEMA = '%s' AND TRIGGER_NAME = '%s';";
private static String TRIGGER_SQL_LIST
= "SELECT TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS where TRIGGER_SCHEMA = '%s';";
private static String SELECT_TABLE_COLUMNS = "SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s' order by ORDINAL_POSITION";
private static String VIEW_SQL
= "SELECT TABLE_SCHEMA AS DatabaseName, TABLE_NAME AS ViewName, VIEW_DEFINITION AS definition, CHECK_OPTION, "
+ "IS_UPDATABLE FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s';";
private List<String> systemDatabases = Arrays.asList("information_schema", "system");
public static String format(String tableName) {
return "`" + tableName + "`";
}
@Override
public List<Database> databases(Connection connection) {
List<Database> list = SQLExecutor.getInstance().execute(connection, "SELECT name FROM system.databases;;", resultSet -> {
List<Database> databases = new ArrayList<>();
try {
while (resultSet.next()) {
String dbName = resultSet.getString("name");
Database database = new Database();
database.setName(dbName);
databases.add(database);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
return databases;
});
return sortDatabase(list, systemDatabases, connection);
}
@Override
public String tableDDL(Connection connection, @NotEmpty String databaseName, String schemaName,
@NotEmpty String tableName) {
String sql = "SHOW CREATE TABLE " + format(databaseName) + "."
+ format(tableName);
return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
if (resultSet.next()) {
return resultSet.getString("Create Table");
}
return null;
});
}
@Override
public Function function(Connection connection, @NotEmpty String databaseName, String schemaName,
String functionName) {
String sql = String.format(ROUTINES_SQL, "FUNCTION", databaseName, functionName);
return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
Function function = new Function();
function.setDatabaseName(databaseName);
function.setSchemaName(schemaName);
function.setFunctionName(functionName);
if (resultSet.next()) {
function.setSpecificName(resultSet.getString("SPECIFIC_NAME"));
function.setRemarks(resultSet.getString("ROUTINE_COMMENT"));
function.setFunctionBody(resultSet.getString("ROUTINE_DEFINITION"));
}
return function;
});
}
@Override
public List<Trigger> triggers(Connection connection, String databaseName, String schemaName) {
List<Trigger> triggers = new ArrayList<>();
String sql = String.format(TRIGGER_SQL_LIST, databaseName);
return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
while (resultSet.next()) {
Trigger trigger = new Trigger();
trigger.setTriggerName(resultSet.getString("TRIGGER_NAME"));
trigger.setSchemaName(schemaName);
trigger.setDatabaseName(databaseName);
triggers.add(trigger);
}
return triggers;
});
}
@Override
public Trigger trigger(Connection connection, @NotEmpty String databaseName, String schemaName,
String triggerName) {
String sql = String.format(TRIGGER_SQL, databaseName, triggerName);
return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
Trigger trigger = new Trigger();
trigger.setDatabaseName(databaseName);
trigger.setSchemaName(schemaName);
trigger.setTriggerName(triggerName);
if (resultSet.next()) {
trigger.setTriggerBody(resultSet.getString("ACTION_STATEMENT"));
}
return trigger;
});
}
@Override
public Procedure procedure(Connection connection, @NotEmpty String databaseName, String schemaName,
String procedureName) {
String sql = String.format(ROUTINES_SQL, "PROCEDURE", databaseName, procedureName);
return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
Procedure procedure = new Procedure();
procedure.setDatabaseName(databaseName);
procedure.setSchemaName(schemaName);
procedure.setProcedureName(procedureName);
if (resultSet.next()) {
procedure.setSpecificName(resultSet.getString("SPECIFIC_NAME"));
procedure.setRemarks(resultSet.getString("ROUTINE_COMMENT"));
procedure.setProcedureBody(resultSet.getString("ROUTINE_DEFINITION"));
}
return procedure;
});
}
@Override
public List<TableColumn> columns(Connection connection, String databaseName, String schemaName, String tableName) {
String sql = String.format(SELECT_TABLE_COLUMNS, databaseName, tableName);
List<TableColumn> 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("COLUMN_NAME"));
column.setName(resultSet.getString("COLUMN_NAME"));
String dataType = resultSet.getString("DATA_TYPE");
if (dataType.startsWith("Nullable(")) {
dataType = dataType.substring(9, dataType.length() - 1);
}
column.setColumnType(dataType);
column.setDefaultValue(resultSet.getString("COLUMN_DEFAULT"));
column.setAutoIncrement(resultSet.getString("EXTRA").contains("auto_increment"));
column.setComment(resultSet.getString("COLUMN_COMMENT"));
column.setNullable("YES".equalsIgnoreCase(resultSet.getString("IS_NULLABLE")) ? 1 : 0);
column.setOrdinalPosition(resultSet.getInt("ORDINAL_POSITION"));
column.setDecimalDigits(resultSet.getInt("NUMERIC_SCALE"));
column.setCharSetName(resultSet.getString("CHARACTER_SET_NAME"));
column.setCollationName(resultSet.getString("COLLATION_NAME"));
setColumnSize(column, resultSet.getString("COLUMN_TYPE"));
tableColumns.add(column);
}
return tableColumns;
});
}
private void setColumnSize(TableColumn column, String columnType) {
try {
if (columnType.contains("(")) {
String size = columnType.substring(columnType.indexOf("(") + 1, columnType.indexOf(")"));
if ("SET".equalsIgnoreCase(column.getColumnType()) || "ENUM".equalsIgnoreCase(column.getColumnType())) {
column.setValue(size);
} else {
if (size.contains(",")) {
String[] sizes = size.split(",");
if (StringUtils.isNotBlank(sizes[0])) {
column.setColumnSize(Integer.parseInt(sizes[0]));
}
if (StringUtils.isNotBlank(sizes[1])) {
column.setDecimalDigits(Integer.parseInt(sizes[1]));
}
} else {
column.setColumnSize(Integer.parseInt(size));
}
}
}
} catch (Exception e) {
}
}
@Override
public Table view(Connection connection, String databaseName, String schemaName, String viewName) {
String sql = String.format(VIEW_SQL, databaseName, viewName);
return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
Table table = new Table();
table.setDatabaseName(databaseName);
table.setSchemaName(schemaName);
table.setName(viewName);
if (resultSet.next()) {
table.setDdl(resultSet.getString("definition"));
}
return table;
});
}
@Override
public List<TableIndex> indexes(Connection connection, String databaseName, String schemaName, String tableName) {
StringBuilder queryBuf = new StringBuilder("SHOW INDEX FROM ");
queryBuf.append("`").append(tableName).append("`");
queryBuf.append(" FROM ");
queryBuf.append("`").append(databaseName).append("`");
return SQLExecutor.getInstance().execute(connection, queryBuf.toString(), resultSet -> {
LinkedHashMap<String, TableIndex> map = new LinkedHashMap();
while (resultSet.next()) {
String keyName = resultSet.getString("Key_name");
TableIndex index = new TableIndex();
index.setDatabaseName(databaseName);
index.setSchemaName(schemaName);
index.setTableName(tableName);
index.setName(keyName);
index.setUnique(!resultSet.getBoolean("Non_unique"));
index.setType(resultSet.getString("Index_type"));
// index.setComment(resultSet.getString("Index_comment"));
List<TableIndexColumn> tableIndexColumns = new ArrayList<>();
tableIndexColumns.addAll(getTableIndexColumn(resultSet));
index.setColumnList(tableIndexColumns);
if ("PRIMARY".equalsIgnoreCase(keyName)) {
index.setType(ClickHouseIndexTypeEnum.PRIMARY.getName());
}
map.put(keyName, index);
}
return map.values().stream().collect(Collectors.toList());
});
}
private List<TableIndexColumn> getTableIndexColumn(ResultSet resultSet) throws SQLException {
List<TableIndexColumn> tableIndexColumns = new ArrayList<>();
String name = StringUtils.isBlank(resultSet.getString("column_name")) ? resultSet.getString("expression") : resultSet.getString("column_name");
if (StringUtils.isNotBlank(name)) {
String[] split = name.split(",");
for (String columName : split) {
TableIndexColumn tableIndexColumn = new TableIndexColumn();
tableIndexColumn.setColumnName(columName);
tableIndexColumn.setOrdinalPosition(resultSet.getShort("seq_in_index"));
tableIndexColumn.setCollation(resultSet.getString("collation"));
tableIndexColumn.setCardinality(resultSet.getLong("cardinality"));
tableIndexColumn.setSubPart(resultSet.getLong("sub_part"));
tableIndexColumns.add(tableIndexColumn);
}
}
return tableIndexColumns;
}
@Override
public SqlBuilder getSqlBuilder() {
return new ClickHouseSqlBuilder();
}
@Override
public TableMeta getTableMeta(String databaseName, String schemaName, String tableName) {
return TableMeta.builder()
.columnTypes(ClickHouseColumnTypeEnum.getTypes())
.engineTypes(ClickHouseEngineTypeEnum.getTypes())
.indexTypes(ClickHouseIndexTypeEnum.getIndexTypes())
.build();
}
@Override
public String getMetaDataName(String... names) {
return Arrays.stream(names).filter(name -> StringUtils.isNotBlank(name)).map(name -> "`" + name + "`").collect(Collectors.joining("."));
}
}

View File

@ -0,0 +1,137 @@
package ai.chat2db.plugin.clickhouse.builder;
import ai.chat2db.plugin.clickhouse.type.ClickHouseColumnTypeEnum;
import ai.chat2db.plugin.clickhouse.type.ClickHouseIndexTypeEnum;
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 org.apache.commons.lang3.StringUtils;
public class ClickHouseSqlBuilder extends DefaultSqlBuilder implements SqlBuilder {
@Override
public String buildCreateTableSql(Table table) {
StringBuilder script = new StringBuilder();
script.append("CREATE TABLE ");
if (StringUtils.isNotBlank(table.getDatabaseName())) {
script.append("`").append(table.getDatabaseName()).append("`").append(".");
}
script.append("`").append(table.getName()).append("`").append(" (").append("\n");
// append column
for (TableColumn column : table.getColumnList()) {
if (StringUtils.isBlank(column.getName()) || StringUtils.isBlank(column.getColumnType())) {
continue;
}
ClickHouseColumnTypeEnum typeEnum = ClickHouseColumnTypeEnum.getByType(column.getColumnType());
script.append("\t").append(typeEnum.buildCreateColumnSql(column)).append(",\n");
}
// append index
for (TableIndex tableIndex : table.getIndexList()) {
if (StringUtils.isBlank(tableIndex.getName()) || StringUtils.isBlank(tableIndex.getType())) {
continue;
}
ClickHouseIndexTypeEnum mysqlIndexTypeEnum = ClickHouseIndexTypeEnum.getByType(tableIndex.getType());
if (!ClickHouseIndexTypeEnum.PRIMARY.equals(mysqlIndexTypeEnum) ) {
script.append("\t").append("").append(mysqlIndexTypeEnum.buildIndexScript(tableIndex)).append(",\n");
}
}
script = new StringBuilder(script.substring(0, script.length() - 2));
script.append("\n)");
if (StringUtils.isNotBlank(table.getEngine())) {
script.append(" ENGINE=").append(table.getEngine()).append("\n");
}
// append primary key
for (TableIndex tableIndex : table.getIndexList()) {
if (StringUtils.isBlank(tableIndex.getName()) || StringUtils.isBlank(tableIndex.getType())) {
continue;
}
ClickHouseIndexTypeEnum mysqlIndexTypeEnum = ClickHouseIndexTypeEnum.getByType(tableIndex.getType());
if (ClickHouseIndexTypeEnum.PRIMARY.equals(mysqlIndexTypeEnum) ) {
script.append("\t").append("").append(mysqlIndexTypeEnum.buildIndexScript(tableIndex)).append("\n");
}
}
if (StringUtils.isNotBlank(table.getComment())) {
script.append(" COMMENT '").append(table.getComment()).append("'");
}
script.append(";");
return script.toString();
}
@Override
public String buildModifyTaleSql(Table oldTable, Table newTable) {
StringBuilder script = new StringBuilder();
script.append("ALTER TABLE ");
if (StringUtils.isNotBlank(oldTable.getDatabaseName())) {
script.append("`").append(oldTable.getDatabaseName()).append("`").append(".");
}
script.append("`").append(oldTable.getName()).append("`").append("\n");
if (!StringUtils.equalsIgnoreCase(oldTable.getComment(), newTable.getComment())) {
script.append("\t").append("MODIFY COMMENT").append("'").append(newTable.getComment()).append("'").append(",\n");
}
// append modify column
for (TableColumn tableColumn : newTable.getColumnList()) {
if (StringUtils.isNotBlank(tableColumn.getEditStatus()) && StringUtils.isNotBlank(tableColumn.getColumnType()) && StringUtils.isNotBlank(tableColumn.getName())) {
ClickHouseColumnTypeEnum typeEnum = ClickHouseColumnTypeEnum.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())) {
ClickHouseIndexTypeEnum mysqlIndexTypeEnum = ClickHouseIndexTypeEnum
.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 sqlBuilder = new StringBuilder(sql.length() + 14);
sqlBuilder.append(sql);
if (offset == 0) {
sqlBuilder.append("\n LIMIT ");
sqlBuilder.append(pageSize);
} else {
sqlBuilder.append("\n LIMIT ");
sqlBuilder.append(offset);
sqlBuilder.append(",");
sqlBuilder.append(pageSize);
}
return sqlBuilder.toString();
}
@Override
public String buildCreateDatabaseSql(Database database) {
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append("CREATE DATABASE `" + database.getName() + "`");
if(StringUtils.isNotBlank(database.getComment())){
sqlBuilder.append(";ALTER DATABASE ").append(database.getName()).append(" COMMENT '").append(database.getComment()).append("';");
}
return sqlBuilder.toString();
}
}

View File

@ -1,7 +1,7 @@
{
"dbType": "CLICKHOUSE",
"supportDatabase": false,
"supportSchema": true,
"supportDatabase": true,
"supportSchema":false,
"driverConfigList": [
{
"url": "jdbc:clickhouse://localhost:8123/",

View File

@ -0,0 +1,221 @@
package ai.chat2db.plugin.clickhouse.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 ClickHouseColumnTypeEnum implements ColumnBuilder {
String("String", false, false, true, false, false, false, true, true, false, false),
Int8("Int8", false, false, true, false, false, false, true, true, false, false),
Int16("Int16", false, false, true, false, false, false, true, true, false, false),
Int32("Int32", false, false, true, false, false, false, true, true, false, false),
Int64("Int64", false, false, true, false, false, false, true, true, false, false),
Int128("Int128", false, false, true, false, false, false, true, true, false, false),
Int256("Int256", false, false, true, false, false, false, true, true, false, false),
UInt8("UInt8", false, false, true, false, false, false, true, true, false, false),
UInt16("UInt16", false, false, true, false, false, false, true, true, false, false),
UInt32("UInt32", false, false, true, false, false, false, true, true, false, false),
UInt64("UInt64", false, false, true, false, false, false, true, true, false, false),
UInt128("UInt128", false, false, true, false, false, false, true, true, false, false),
UInt256("UInt256", false, false, true, false, false, false, true, true, false, false),
Float32("Float32", false, false, true, false, false, false, true, true, false, false),
Float64("Float64", false, false, true, false, false, false, true, true, false, false),
Decimal("Decimal", true, true, true, false, false, false, true, true, false, false),
Boolean("Boolean", false, false, true, false, false, false, true, true, false, false),
FixedString("FixedString", false, false, true, false, false, false, true, true, false, false),
UUID("UUID", false, false, true, false, false, false, true, true, false, false),
Date("Date", false, false, true, false, false, false, true, true, false, false),
DATE32("DATE32", false, false, true, false, false, false, true, true, false, false),
DateTime("DateTime", false, false, true, false, false, false, true, true, false, false),
DateTime64("DateTime64", false, false, true, false, false, false, true, true, false, false),
Enum8("Enum8", false, false, true, false, false, false, true, true, false, false),
Enum16("Enum16", false, false, true, false, false, false, true, true, false, false),
Array("Array", false, false, false, false, false, false, true, true, false, false),
JSON("JSON", false, false, true, false, false, false, true, true, false, false),
Nested("Nested", false, false, true, false, false, false, true, true, false, false),
Map("Map", true, true, true, false, false, false, true, true, false, false),
IPv4("IPv4", false, false, true, false, false, false, true, true, false, false),
IPv6("IPv6", false, false, true, false, false, false, true, true, false, false),
Point("Point", false, false, true, false, false, false, true, true, false, false),
Ring("Ring", false, false, true, false, false, false, true, true, false, false),
Polygon("Polygon", false, false, true, false, false, false, true, true, false, false),
MultiPolygon("MultiPolygon", false, false, true, false, false, false, true, true, false, false),
AggregateFunction("AggregateFunction", true, true, true, false, false, false, true, true, false, false),
SimpleAggregateFunction("SimpleAggregateFunction", true, true, true, false, false, false, true, true, false, false),
;
private static Map<String, ClickHouseColumnTypeEnum> COLUMN_TYPE_MAP = Maps.newHashMap();
static {
for (ClickHouseColumnTypeEnum value : ClickHouseColumnTypeEnum.values()) {
COLUMN_TYPE_MAP.put(value.getColumnType().getTypeName(), value);
}
}
private ColumnType columnType;
ClickHouseColumnTypeEnum(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, false);
}
public static ClickHouseColumnTypeEnum getByType(String dataType) {
return COLUMN_TYPE_MAP.get(dataType);
}
public static List<ColumnType> getTypes() {
return Arrays.stream(ClickHouseColumnTypeEnum.values()).map(columnTypeEnum ->
columnTypeEnum.getColumnType()
).toList();
}
public ColumnType getColumnType() {
return columnType;
}
@Override
public String buildCreateColumnSql(TableColumn column) {
ClickHouseColumnTypeEnum type = COLUMN_TYPE_MAP.get(column.getColumnType());
if (type == null) {
return "";
}
StringBuilder script = new StringBuilder();
script.append("`").append(column.getName()).append("`").append(" ");
script.append(buildNullableAndDataType(column, type)).append(" ");
script.append(buildDefaultValue(column, type)).append(" ");
script.append(buildComment(column, type)).append(" ");
return script.toString();
}
@Override
public String buildModifyColumn(TableColumn tableColumn) {
if (EditStatus.DELETE.name().equals(tableColumn.getEditStatus())) {
return StringUtils.join("DROP COLUMN `", tableColumn.getName() + "`");
}
if (EditStatus.ADD.name().equals(tableColumn.getEditStatus())) {
return StringUtils.join("ADD COLUMN ", buildCreateColumnSql(tableColumn));
}
if (EditStatus.MODIFY.name().equals(tableColumn.getEditStatus())) {
String modifyColumn = "";
if (!StringUtils.equalsIgnoreCase(tableColumn.getOldName(), tableColumn.getName())) {
modifyColumn = StringUtils.join("RENAME COLUMN `", tableColumn.getOldName(), "` TO `", tableColumn.getName(),
"`, ", buildCreateColumnSql(tableColumn));
}
return StringUtils.join(modifyColumn, "MODIFY COLUMN ", buildCreateColumnSql(tableColumn));
}
return "";
}
private String buildComment(TableColumn column, ClickHouseColumnTypeEnum type) {
if (!type.columnType.isSupportComments() || StringUtils.isEmpty(column.getComment())) {
return "";
}
return StringUtils.join("COMMENT '", column.getComment(), "'");
}
private String buildDefaultValue(TableColumn column, ClickHouseColumnTypeEnum 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");
}
if (Arrays.asList(Enum8,Enum16).contains(type)) {
return StringUtils.join("DEFAULT '", column.getDefaultValue(), "'");
}
if (Arrays.asList(Date).contains(type)) {
return StringUtils.join("DEFAULT '", column.getDefaultValue(), "'");
}
if (Arrays.asList(DateTime).contains(type)) {
if ("CURRENT_TIMESTAMP".equalsIgnoreCase(column.getDefaultValue().trim())) {
return StringUtils.join("DEFAULT ", column.getDefaultValue());
}
return StringUtils.join("DEFAULT '", column.getDefaultValue(), "'");
}
return StringUtils.join("DEFAULT ", column.getDefaultValue());
}
private String buildNullableAndDataType(TableColumn column, ClickHouseColumnTypeEnum type) {
StringBuilder script = new StringBuilder();
script.append(buildDataType(column, type));
if (!type.getColumnType().isSupportNullable()) {
return script.toString();
}
if (column.getNullable() != null && 1 == column.getNullable()) {
return "Nullable("+script.append(")").toString();
} else {
return script.toString();
}
}
private String buildDataType(TableColumn column, ClickHouseColumnTypeEnum type) {
String columnType = type.columnType.getTypeName();
if (Arrays.asList(FixedString).contains(type)) {
return StringUtils.join(columnType, "(", column.getColumnSize(), ")");
}
if (Arrays.asList(Decimal).contains(type)) {
if (column.getColumnSize() == null || column.getDecimalDigits() == null) {
return columnType;
}
if (column.getColumnSize() != null && column.getDecimalDigits() == null) {
return StringUtils.join(columnType, "(", column.getColumnSize() + ")");
}
if (column.getColumnSize() != null && column.getDecimalDigits() != null) {
return StringUtils.join(columnType, "(", column.getColumnSize() + "," + column.getDecimalDigits() + ")");
}
}
return columnType;
}
public String buildColumn(TableColumn column) {
ClickHouseColumnTypeEnum type = COLUMN_TYPE_MAP.get(column.getColumnType());
if (type == null) {
return "";
}
StringBuilder script = new StringBuilder();
script.append("`").append(column.getName()).append("`").append(" ");
script.append(buildDataType(column, type)).append(" ");
if (StringUtils.isNoneBlank(column.getComment())) {
script.append("COMMENT").append(" ").append("'").append(column.getComment()).append("'").append(" ");
}
return script.toString();
}
private String unsignedDataType(String dataTypeName, String middle) {
String[] split = dataTypeName.split(" ");
if (split.length == 2) {
return StringUtils.join(split[0], middle, split[1]);
}
return StringUtils.join(dataTypeName, middle);
}
}

View File

@ -0,0 +1,101 @@
package ai.chat2db.plugin.clickhouse.type;
import ai.chat2db.spi.model.EngineType;
import com.google.common.collect.Maps;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public enum ClickHouseEngineTypeEnum {
AzureBlobStorage("AzureBlobStorage",false,true,false,false,true,false,false,false ),
KeeperMap("KeeperMap",false,true,false,false,false,true,false,false ),
SQLite("SQLite",false,false,false,false,false,false,false,false ),
ExternalDistributed("ExternalDistributed",false,false,false,false,false,false,false,false ),
PostgreSQL("PostgreSQL",false,false,false,false,false,false,false,false ),
NATS("NATS",false,false,false,false,true,false,false,false ),
RabbitMQ("RabbitMQ",false,false,false,false,true,false,false,false ),
Kafka("Kafka",false,false,false,false,true,false,false,false ),
MongoDB("MongoDB",false,false,false,false,false,false,false,false ),
FileLog("FileLog",false,false,false,false,true,false,false,false ),
Dictionary("Dictionary",false,false,false,false,false,false,false,false ),
MySQL("MySQL",false,false,false,false,true,false,false,false ),
S3Queue("S3Queue",false,false,false,false,true,false,false,false ),
HDFS("HDFS",false,true,false,false,false,false,false,false ),
MaterializedPostgreSQL("MaterializedPostgreSQL",false,true,false,false,true,false,false,false ),
S3("S3",false,true,false,false,true,false,false,false ),
FuzzJSON("FuzzJSON",false,false,false,false,false,false,false,false ),
OSS("OSS",false,true,false,false,true,false,false,false ),
WindowView("WindowView",false,false,false,false,false,false,false,false ),
Distributed("Distributed",false,false,false,false,true,true,false,false ),
ReplicatedSummingMergeTree("ReplicatedSummingMergeTree",true,true,true,true,true,true,true,true ),
ExecutablePool("ExecutablePool",false,false,false,false,true,false,false,false ),
COSN("COSN",false,true,false,false,true,false,false,false ),
Iceberg("Iceberg",false,false,false,false,false,false,false,false ),
MaterializedView("MaterializedView",false,false,false,false,false,false,false,false ),
View("View",false,false,false,false,false,false,false,false ),
JDBC("JDBC",false,false,false,false,false,false,false,false ),
Join("Join",false,false,false,false,true,false,false,false ),
Executable("Executable",false,false,false,false,true,false,false,false ),
Set("Set",false,false,false,false,true,false,false,false ),
Redis("Redis",false,true,false,false,false,true,false,false ),
GenerateRandom("GenerateRandom",false,false,false,false,false,false,false,false ),
LiveView("LiveView",false,false,false,false,false,false,false,false ),
MergeTree("MergeTree",true,true,true,false,true,true,true,false ),
ReplicatedReplacingMergeTree("ReplicatedReplacingMergeTree",true,true,true,true,true,true,true,true ),
Memory("Memory",false,false,false,false,true,true,false,false ),
Buffer("Buffer",false,false,false,false,false,true,false,false ),
URL("URL",false,false,false,false,true,false,false,false ),
ReplicatedVersionedCollapsingMergeTree("ReplicatedVersionedCollapsingMergeTree",true,true,true,true,true,true,true,true ),
VersionedCollapsingMergeTree("VersionedCollapsingMergeTree",true,true,true,false,true,true,true,false ),
Hive("Hive",false,true,false,false,true,false,false,false ),
ReplacingMergeTree("ReplacingMergeTree",true,true,true,false,true,true,true,false ),
ReplicatedAggregatingMergeTree("ReplicatedAggregatingMergeTree",true,true,true,true,true,true,true,true ),
ReplicatedMergeTree("ReplicatedMergeTree",true,true,true,true,true,true,true,true ),
DeltaLake("DeltaLake",false,false,false,false,false,false,false,false ),
EmbeddedRocksDB("EmbeddedRocksDB",true,true,false,false,false,true,false,false ),
ReplicatedCollapsingMergeTree("ReplicatedCollapsingMergeTree",true,true,true,true,true,true,true,true ),
File("File",false,false,false,false,true,false,false,false ),
TinyLog("TinyLog",false,false,false,false,true,false,false,false ),
ReplicatedGraphiteMergeTree("ReplicatedGraphiteMergeTree",true,true,true,true,true,true,true,true ),
SummingMergeTree("SummingMergeTree",true,true,true,false,true,true,true,false ),
Hudi("Hudi",false,false,false,false,false,false,false,false ),
GraphiteMergeTree("GraphiteMergeTree",true,true,true,false,true,true,true,false ),
CollapsingMergeTree("CollapsingMergeTree",true,true,true,false,true,true,true,false ),
Merge("Merge",false,false,false,false,false,false,false,false ),
AggregatingMergeTree("AggregatingMergeTree",true,true,true,false,true,true,true,false ),
ODBC("ODBC",false,false,false,false,false,false,false,false ),
Null("Null",false,false,false,false,false,true,false,false ),
StripeLog("StripeLog",false,false,false,false,true,false,false,false ),
Log("Log",false,false,false,false,true,false,false,false ),
;
private static Map<String, ClickHouseEngineTypeEnum> ENGINE_TYPE_MAP = Maps.newHashMap();
static {
for (ClickHouseEngineTypeEnum value : ClickHouseEngineTypeEnum.values()) {
ENGINE_TYPE_MAP.put(value.getEngineType().getName(), value);
}
}
private EngineType engineType;
ClickHouseEngineTypeEnum(String name, boolean supportTTL, boolean supportSortOrder, boolean supportSkippingIndices, boolean supportDeduplication, boolean supportSettings, boolean supportParallelInsert, boolean supportProjections, boolean supportReplication) {
this.engineType = new EngineType(name, supportTTL, supportSortOrder, supportSkippingIndices, supportDeduplication, supportSettings, supportParallelInsert, supportProjections, supportReplication);
}
public static ClickHouseEngineTypeEnum getByType(String dataType) {
return ENGINE_TYPE_MAP.get(dataType.toUpperCase());
}
public static List<EngineType> getTypes() {
return Arrays.stream(ClickHouseEngineTypeEnum.values()).map(engineTypeEnum ->
engineTypeEnum.getEngineType()
).toList();
}
public EngineType getEngineType() {
return engineType;
}
}

View File

@ -0,0 +1,123 @@
package ai.chat2db.plugin.clickhouse.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 ClickHouseIndexTypeEnum {
PRIMARY("Primary", "PRIMARY KEY"),
MINMAX("MINMAX", "INDEX"),
SET("SET", "INDEX"),
BLOOM_FILTER("BLOOM_FILTER", "INDEX"),
TOKENBF_V1("TOKENBF_V1", "INDEX"),
NGRAMBF_V1("NGRAMBF_V1", "INDEX"),
INVERTED("INVERTED", "INDEX"),
HYPOTHESIS("HYPOTHESIS", "INDEX"),
ANNOY("ANNOY", "INDEX"),
USEARCH("USEARCH", "INDEX"),
;
private String name;
private String keyword;
private IndexType indexType;
ClickHouseIndexTypeEnum(String name, String keyword) {
this.name = name;
this.keyword = keyword;
this.indexType = new IndexType(name);
}
public static ClickHouseIndexTypeEnum getByType(String type) {
for (ClickHouseIndexTypeEnum value : ClickHouseIndexTypeEnum.values()) {
if (value.name.equalsIgnoreCase(type)) {
return value;
}
}
return null;
}
public static List<IndexType> getIndexTypes() {
return Arrays.asList(ClickHouseIndexTypeEnum.values()).stream().map(ClickHouseIndexTypeEnum::getIndexType).collect(java.util.stream.Collectors.toList());
}
public String getName() {
return name;
}
public String getKeyword() {
return keyword;
}
public IndexType getIndexType() {
return indexType;
}
public void setIndexType(IndexType indexType) {
this.indexType = indexType;
}
public String buildIndexScript(TableIndex tableIndex) {
StringBuilder script = new StringBuilder();
script.append(keyword).append(" ");
script.append(buildIndexName(tableIndex)).append(" ");
script.append(buildIndexColumn(tableIndex)).append(" ");
script.append(buildIndexType(tableIndex)).append(" ");
return script.toString();
}
private String buildIndexType(TableIndex tableIndex) {
if (this.equals(PRIMARY)) {
return "";
} else {
return "TYPE " + name ;
}
}
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("`");
script.append(",");
}
}
script.deleteCharAt(script.length() - 1);
script.append(")");
return script.toString();
}
private String buildIndexName(TableIndex tableIndex) {
if (this.equals(PRIMARY)) {
return "";
} else {
return "`" + tableIndex.getName() + "`";
}
}
public String buildModifyIndex(TableIndex tableIndex) {
if (this.equals(PRIMARY)) {
return "";
}
if (EditStatus.DELETE.name().equals(tableIndex.getEditStatus())) {
return StringUtils.join("DROP INDEX `", tableIndex.getOldName(), "`");
}
if (EditStatus.MODIFY.name().equals(tableIndex.getEditStatus())) {
return StringUtils.join("DROP INDEX `", tableIndex.getOldName(),
"`,\n ADD ", buildIndexScript(tableIndex));
}
if (EditStatus.ADD.name().equals(tableIndex.getEditStatus())) {
return StringUtils.join("ADD ", buildIndexScript(tableIndex));
}
return "";
}
}