Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ai.chat2db.plugin.clickhouse;

import ai.chat2db.spi.IDbManager;
import ai.chat2db.plugin.clickhouse.identifier.ClickHouseIdentifierProcessor;
import ai.chat2db.spi.DefaultDBManager;
import ai.chat2db.community.domain.api.model.async.AsyncContext;
import ai.chat2db.spi.model.datasource.ConnectInfo;
Expand All @@ -11,6 +12,8 @@
import org.slf4j.LoggerFactory;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import static ai.chat2db.plugin.clickhouse.constant.ClickHouseDBManagerConstants.*;
Expand All @@ -37,7 +40,7 @@ private void exportFunctions(Connection connection, AsyncContext asyncContext) t
try (PreparedStatement preparedStatement = connection.prepareStatement(sql); ResultSet resultSet = preparedStatement.executeQuery()) {
while (resultSet.next()) {
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append(SQL_DROP_FUNCTION_EXISTS).append(resultSet.getString("name")).append(";")
sqlBuilder.append(SQL_DROP_FUNCTION_EXISTS).append(ClickHouseIdentifierProcessor.INSTANCE.quoteIdentifierAlways(resultSet.getString("name"))).append(";")
.append("\n")
.append(resultSet.getString("create_query")).append(";").append("\n");
asyncContext.write(sqlBuilder.toString());
Expand All @@ -46,7 +49,7 @@ private void exportFunctions(Connection connection, AsyncContext asyncContext) t
}

private void exportTablesOrViewsOrDictionaries(Connection connection, String databaseName, String schemaName, AsyncContext asyncContext) throws SQLException {
String sql = String.format(SQL_SELECT_CREATE_TABLE_QUERY_HAS, databaseName);
String sql = String.format(SQL_SELECT_CREATE_TABLE_QUERY_HAS, ClickHouseIdentifierProcessor.INSTANCE.escapeString(databaseName));
try (PreparedStatement statement = connection.prepareStatement(sql); ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {

Expand All @@ -56,17 +59,17 @@ private void exportTablesOrViewsOrDictionaries(Connection connection, String dat
String tableOrViewName = resultSet.getString("name");
if (Objects.equals("View", tableType)) {
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append(SQL_DROP_VIEW_EXISTS).append(databaseName).append(".").append(tableOrViewName)
sqlBuilder.append(SQL_DROP_VIEW_EXISTS).append(ClickHouseIdentifierProcessor.INSTANCE.quoteIdentifierAlways(databaseName)).append(".").append(ClickHouseIdentifierProcessor.INSTANCE.quoteIdentifierAlways(tableOrViewName))
.append(";").append("\n").append(ddl).append(";").append("\n");
asyncContext.write(sqlBuilder.toString());
} else if (Objects.equals("Dictionary", tableType)) {
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append(SQL_DROP_DICTIONARY_EXISTS).append(databaseName).append(".").append(tableOrViewName)
sqlBuilder.append(SQL_DROP_DICTIONARY_EXISTS).append(ClickHouseIdentifierProcessor.INSTANCE.quoteIdentifierAlways(databaseName)).append(".").append(ClickHouseIdentifierProcessor.INSTANCE.quoteIdentifierAlways(tableOrViewName))
.append(";").append("\n").append(ddl).append(";").append("\n");
asyncContext.write(sqlBuilder.toString());
} else {
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append(SQL_DROP_TABLE_EXISTS).append(databaseName).append(".").append(tableOrViewName)
sqlBuilder.append(SQL_DROP_TABLE_EXISTS).append(ClickHouseIdentifierProcessor.INSTANCE.quoteIdentifierAlways(databaseName)).append(".").append(ClickHouseIdentifierProcessor.INSTANCE.quoteIdentifierAlways(tableOrViewName))
.append(";").append("\n").append(ddl).append(";").append("\n");
asyncContext.write(sqlBuilder.toString());
if (asyncContext.isContainsData() && dataFlag) {
Expand Down Expand Up @@ -112,18 +115,48 @@ private String setDatabaseInJdbcUrl(ConnectInfo connectInfo) {

@Override
public String dropTable(Connection connection, String databaseName, String schemaName, String tableName) {
String sql = "DROP TABLE IF EXISTS " + ClickHouseMetaData.format(schemaName) + "." + ClickHouseMetaData.format(tableName);
return sql;
return "DROP TABLE IF EXISTS " + qualifiedTableName(databaseName, schemaName, tableName, false);
}

@Override
public String truncateTable(Connection connection, String databaseName, String schemaName, String tableName) {
return "TRUNCATE TABLE " + qualifiedTableName(databaseName, schemaName, tableName, true);
}

@Override
public void copyTable(Connection connection, String databaseName, String schemaName, String tableName, String newTableName, boolean copyData) throws SQLException {
String sql = "CREATE TABLE " + newTableName + " AS " + tableName + "";
DefaultSQLExecutor.getInstance().execute(connection, sql, resultSet -> null);
if (copyData) {
sql = "INSERT INTO " + newTableName + " SELECT * FROM " + tableName;
for (String sql : buildCopyTableStatements(databaseName, schemaName, tableName, newTableName, copyData)) {
DefaultSQLExecutor.getInstance().execute(connection, sql, resultSet -> null);
}
}

static List<String> buildCopyTableStatements(String databaseName, String schemaName, String tableName,
String newTableName, boolean copyData) {
String source = qualifiedTableName(databaseName, schemaName, tableName, true);
String target = qualifiedTableName(databaseName, schemaName, newTableName, true);
List<String> statements = new ArrayList<>();
statements.add("CREATE TABLE " + target + " AS " + source);
if (copyData) {
statements.add("INSERT INTO " + target + " SELECT * FROM " + source);
}
return statements;
}

private static String qualifiedTableName(String databaseName, String schemaName, String tableName,
boolean normalizeQuotedTable) {
String qualifier = StringUtils.isNotBlank(schemaName) ? schemaName : databaseName;
String normalizedTable = normalizeQuotedTable ? normalizeQuotedIdentifier(tableName) : tableName;
if (StringUtils.isBlank(qualifier)) {
return ClickHouseIdentifierProcessor.INSTANCE.quoteIdentifierAlways(normalizedTable);
}
return ClickHouseIdentifierProcessor.INSTANCE.quoteIdentifierAlways(qualifier)
+ "." + ClickHouseIdentifierProcessor.INSTANCE.quoteIdentifierAlways(normalizedTable);
}

private static String normalizeQuotedIdentifier(String identifier) {
if (ClickHouseIdentifierProcessor.INSTANCE.isQuoteIdentifier(identifier)) {
return ClickHouseIdentifierProcessor.INSTANCE.removeIdentifierQuote(identifier);
}
return identifier;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
import ai.chat2db.plugin.clickhouse.enums.type.ClickHouseColumnTypeEnum;
import ai.chat2db.plugin.clickhouse.enums.type.ClickHouseEngineTypeEnum;
import ai.chat2db.plugin.clickhouse.enums.type.ClickHouseIndexTypeEnum;
import ai.chat2db.plugin.clickhouse.identifier.ClickHouseIdentifierProcessor;
import ai.chat2db.spi.ICommandExecutor;
import ai.chat2db.spi.IDbMetaData;
import ai.chat2db.spi.ISQLIdentifierProcessor;
import ai.chat2db.spi.ISqlBuilder;
import ai.chat2db.spi.DefaultMetaService;
import ai.chat2db.community.domain.api.model.account.*;
Expand Down Expand Up @@ -38,8 +40,13 @@
public class ClickHouseMetaData extends DefaultMetaService implements IDbMetaData {
private static final Logger log = LoggerFactory.getLogger(ClickHouseMetaData.class);

@Override
public ISQLIdentifierProcessor getSQLIdentifierProcessor() {
return ClickHouseIdentifierProcessor.INSTANCE;
}

public static String format(String tableName) {
return "`" + tableName + "`";
return ClickHouseIdentifierProcessor.INSTANCE.quoteIdentifierAlways(tableName);
}

@Override
Expand Down Expand Up @@ -86,7 +93,7 @@ public List<Table> tables(Connection connection, String databaseName, String sch

@Override
public List<Table> views(Connection connection, String databaseName, String schemaName) {
String sql = "select name from system.`tables` WHERE `database`='" + schemaName + "' and engine='View'";
String sql = "select name from system.`tables` WHERE `database`='" + getSQLIdentifierProcessor().escapeString(schemaName) + "' and engine='View'";
return DefaultSQLExecutor.getInstance().execute(connection, sql, resultSet -> {
List<Table> tables = new ArrayList<>();
try {
Expand All @@ -108,8 +115,7 @@ public List<Table> views(Connection connection, String databaseName, String sche
@Override
public String tableDDL(Connection connection, @NotEmpty String databaseName, String schemaName,
@NotEmpty String tableName) {
String sql = "SHOW CREATE TABLE " + format(schemaName) + "."
+ format(tableName);
String sql = "SHOW CREATE TABLE " + getMetaDataName(schemaName, tableName);
return DefaultSQLExecutor.getInstance().execute(connection, sql, resultSet -> {
if (resultSet.next()) {
return resultSet.getString("statement");
Expand Down Expand Up @@ -139,7 +145,7 @@ public Function function(Connection connection, @NotEmpty String databaseName, S
@Override
public List<Trigger> triggers(Connection connection, String databaseName, String schemaName) {
List<Trigger> triggers = new ArrayList<>();
String sql = String.format(TRIGGER_SQL_LIST, schemaName);
String sql = String.format(TRIGGER_SQL_LIST, getSQLIdentifierProcessor().escapeString(schemaName));
return DefaultSQLExecutor.getInstance().execute(connection, sql, resultSet -> {
while (resultSet.next()) {
Trigger trigger = new Trigger();
Expand All @@ -156,7 +162,7 @@ public List<Trigger> triggers(Connection connection, String databaseName, String
public Trigger trigger(Connection connection, @NotEmpty String databaseName, String schemaName,
String triggerName) {

String sql = String.format(TRIGGER_SQL, schemaName, triggerName);
String sql = String.format(TRIGGER_SQL, getSQLIdentifierProcessor().escapeString(schemaName), getSQLIdentifierProcessor().escapeString(triggerName));
return DefaultSQLExecutor.getInstance().execute(connection, sql, resultSet -> {
Trigger trigger = new Trigger();
trigger.setDatabaseName(databaseName);
Expand All @@ -172,7 +178,7 @@ public Trigger trigger(Connection connection, @NotEmpty String databaseName, Str
@Override
public Procedure procedure(Connection connection, @NotEmpty String databaseName, String schemaName,
String procedureName) {
String sql = String.format(ROUTINES_SQL, "PROCEDURE", schemaName, procedureName);
String sql = String.format(ROUTINES_SQL, "PROCEDURE", getSQLIdentifierProcessor().escapeString(schemaName), getSQLIdentifierProcessor().escapeString(procedureName));
return DefaultSQLExecutor.getInstance().execute(connection, sql, resultSet -> {
Procedure procedure = new Procedure();
procedure.setDatabaseName(databaseName);
Expand All @@ -189,7 +195,7 @@ public Procedure procedure(Connection connection, @NotEmpty String databaseName,

@Override
public List<TableColumn> columns(Connection connection, String databaseName, String schemaName, String tableName) {
String sql = String.format(SELECT_TABLE_COLUMNS, tableName, schemaName);
String sql = String.format(SELECT_TABLE_COLUMNS, getSQLIdentifierProcessor().escapeString(tableName), getSQLIdentifierProcessor().escapeString(schemaName));
List<TableColumn> tableColumns = new ArrayList<>();

return DefaultSQLExecutor.getInstance().execute(connection, sql, resultSet -> {
Expand Down Expand Up @@ -245,7 +251,7 @@ private void setColumnSize(TableColumn column, String columnType) {

@Override
public Table view(Connection connection, String databaseName, String schemaName, String viewName) {
String sql = String.format(VIEW_SQL, schemaName, viewName);
String sql = String.format(VIEW_SQL, getSQLIdentifierProcessor().escapeString(schemaName), getSQLIdentifierProcessor().escapeString(viewName));
return DefaultSQLExecutor.getInstance().execute(connection, sql, resultSet -> {
Table table = new Table();
table.setDatabaseName(databaseName);
Expand Down Expand Up @@ -298,7 +304,7 @@ public TableMeta getTableMeta(String databaseName, String schemaName, String tab

@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).filter(name -> StringUtils.isNotBlank(name)).map(ClickHouseIdentifierProcessor.INSTANCE::quoteIdentifierAlways).collect(Collectors.joining("."));

}

Expand Down
Loading
Loading