Skip to content
Open
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
Expand Up @@ -115,9 +115,27 @@ public Datasource createDatasource(Datasource datasource) {

@Override
public Datasource updateDatasource(Integer id, Datasource datasource) {
// Regenerate connection URL
// 当连接参数发生变化时,需要重新生成连接 URL,避免继续保存客户端带回来的旧 URL。
// 参数未变化时仍保留 resolveConnectionUrl 的行为,避免覆盖用户手动填写的自定义 URL。
DatasourceTypeHandler handler = datasourceTypeHandlerRegistry.getRequired(datasource.getType());
String connectionUrl = handler.resolveConnectionUrl(datasource);
Datasource existing = datasourceMapper.selectById(id);
String connectionUrl;
if (existing != null && handler.hasRequiredConnectionFields(datasource)
&& connectionParamsChanged(existing, datasource)) {
// 仅采用 handler 能完整拼出、且不会把复合 databaseName 泄漏进 URL 的重建结果。
// 例如当前 Oracle 的 databaseName 可能是 "service|schema",#509 合并前朴素重建会把
// "|schema" 拼进 URL;此时继续保留 resolveConnectionUrl 的结果。
String rebuilt = handler.buildConnectionUrl(datasource);
if (StringUtils.isNotBlank(rebuilt) && !rebuilt.contains("|")) {
connectionUrl = rebuilt;
}
else {
connectionUrl = handler.resolveConnectionUrl(datasource);
}
}
else {
connectionUrl = handler.resolveConnectionUrl(datasource);
}
if (StringUtils.isNotBlank(connectionUrl)) {
datasource.setConnectionUrl(connectionUrl);
}
Expand All @@ -135,6 +153,16 @@ public Datasource updateDatasource(Integer id, Datasource datasource) {
return datasource;
}

/**
* 判断会影响自动生成连接 URL 的参数是否发生变化。
*/
private boolean connectionParamsChanged(Datasource existing, Datasource incoming) {
return !Objects.equals(existing.getType(), incoming.getType())
|| !Objects.equals(existing.getHost(), incoming.getHost())
|| !Objects.equals(existing.getPort(), incoming.getPort())
|| !Objects.equals(existing.getDatabaseName(), incoming.getDatabaseName());
}

@Override
@Transactional
public void deleteDatasource(Integer id) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
import com.alibaba.cloud.ai.dataagent.mapper.DatasourceMapper;
import com.alibaba.cloud.ai.dataagent.mapper.LogicalRelationMapper;
import com.alibaba.cloud.ai.dataagent.service.datasource.handler.DatasourceTypeHandler;
import com.alibaba.cloud.ai.dataagent.service.datasource.handler.impl.MysqlDatasourceTypeHandler;
import com.alibaba.cloud.ai.dataagent.service.datasource.handler.impl.OracleDatasourceTypeHandler;
import com.alibaba.cloud.ai.dataagent.service.datasource.handler.impl.PostgreSqlDatasourceTypeHandler;
import com.alibaba.cloud.ai.dataagent.service.datasource.handler.registry.DatasourceTypeHandlerRegistry;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -423,6 +426,126 @@ void updateDatasource_nullUsernameAndPassword_setsEmpty() {
assertEquals("", result.getPassword());
}

@Test
void updateDatasource_rebuildsConnectionUrlWhenDatabaseNameChanges() {
// #345:修改数据库名后,即使客户端带回旧 URL,也要按新的连接参数重新生成。
Datasource existing = Datasource.builder()
.id(5)
.type("mysql")
.host("localhost")
.port(3306)
.databaseName("olddb")
.connectionUrl("jdbc:mysql://localhost:3306/olddb")
.build();
when(datasourceMapper.selectById(5)).thenReturn(existing);
when(handlerRegistry.getRequired("mysql")).thenReturn(new MysqlDatasourceTypeHandler());

Datasource incoming = Datasource.builder()
.type("mysql")
.host("localhost")
.port(3306)
.databaseName("newdb")
.connectionUrl("jdbc:mysql://localhost:3306/olddb")
.build();

Datasource result = datasourceService.updateDatasource(5, incoming);

assertTrue(result.getConnectionUrl().contains("/newdb"),
"URL should reflect the new database name but was: " + result.getConnectionUrl());
assertFalse(result.getConnectionUrl().contains("/olddb"), "URL should not keep the old database name");
verify(datasourceMapper).updateById(incoming);
}

@Test
void updateDatasource_preservesCustomUrlWhenParamsUnchanged() {
// 连接参数未变化时,认为 connectionUrl 是用户手动填写的自定义 URL,不主动覆盖。
Datasource existing = Datasource.builder()
.id(7)
.type("mysql")
.host("localhost")
.port(3306)
.databaseName("db")
.connectionUrl("jdbc:mysql://localhost:3306/db")
.build();
when(datasourceMapper.selectById(7)).thenReturn(existing);
when(handlerRegistry.getRequired("mysql")).thenReturn(new MysqlDatasourceTypeHandler());

String customUrl = "jdbc:mysql://localhost:3306/db?useSSL=true&customParam=keepme";
Datasource incoming = Datasource.builder()
.type("mysql")
.host("localhost")
.port(3306)
.databaseName("db")
.connectionUrl(customUrl)
.build();

Datasource result = datasourceService.updateDatasource(7, incoming);

assertEquals(customUrl, result.getConnectionUrl());
verify(datasourceMapper).updateById(incoming);
}

@Test
void updateDatasource_preservesUrlForCompositeDatabaseName() {
// Oracle 把 schema 编码进 databaseName("service|schema"),朴素重建会把
// "|schema" 残留进 URL,因此这类结果保留原 URL(完整重建见 #509)。
Datasource existing = Datasource.builder()
.id(9)
.type("oracle")
.host("orahost")
.port(1521)
.databaseName("orcl|HR")
.connectionUrl("jdbc:oracle:thin:@orahost:1521/orcl")
.build();
when(datasourceMapper.selectById(9)).thenReturn(existing);
when(handlerRegistry.getRequired("oracle")).thenReturn(new OracleDatasourceTypeHandler());

Datasource incoming = Datasource.builder()
.type("oracle")
.host("orahost")
.port(1521)
.databaseName("orcl|SCOTT")
.connectionUrl("jdbc:oracle:thin:@orahost:1521/orcl")
.build();

Datasource result = datasourceService.updateDatasource(9, incoming);

assertEquals("jdbc:oracle:thin:@orahost:1521/orcl", result.getConnectionUrl());
verify(datasourceMapper).updateById(incoming);
}

@Test
void updateDatasource_rebuildsUrlForPostgresCompositeDatabaseName() {
// PostgreSQL 的 databaseName 同样是 "db|schema",但 buildConnectionUrl 会切出 db。
// 重建结果不含 "|" 时应正常采用,避免误伤 PostgreSQL 的 #345 场景。
Datasource existing = Datasource.builder()
.id(11)
.type("postgresql")
.host("pghost")
.port(5432)
.databaseName("olddb|public")
.connectionUrl("jdbc:postgresql://pghost:5432/olddb")
.build();
when(datasourceMapper.selectById(11)).thenReturn(existing);
when(handlerRegistry.getRequired("postgresql")).thenReturn(new PostgreSqlDatasourceTypeHandler());

Datasource incoming = Datasource.builder()
.type("postgresql")
.host("pghost")
.port(5432)
.databaseName("newdb|public")
.connectionUrl("jdbc:postgresql://pghost:5432/olddb")
.build();

Datasource result = datasourceService.updateDatasource(11, incoming);

assertTrue(result.getConnectionUrl().contains("/newdb"),
"URL should reflect the new database but was: " + result.getConnectionUrl());
assertFalse(result.getConnectionUrl().contains("olddb"), "URL should not keep the old database");
assertFalse(result.getConnectionUrl().contains("|"), "schema separator must not leak into the URL");
verify(datasourceMapper).updateById(incoming);
}

@Test
void testConnection_pingFails_returnsFalse() {
Datasource ds = Datasource.builder().id(1).name("test").type("mysql").username("user").password("pass").build();
Expand Down
Loading