feat: add support starrocks and multiple catalog (#2066)

This commit is contained in:
Hyman_bz
2025-02-19 18:00:22 +08:00
committed by GitHub
parent 86b9d2013a
commit 33268bf3d9
12 changed files with 183 additions and 16 deletions

View File

@@ -9,7 +9,8 @@ public enum EngineType {
POSTGRESQL(6, "POSTGRESQL"), POSTGRESQL(6, "POSTGRESQL"),
OTHER(7, "OTHER"), OTHER(7, "OTHER"),
DUCKDB(8, "DUCKDB"), DUCKDB(8, "DUCKDB"),
HANADB(9, "HANADB"); HANADB(9, "HANADB"),
STARROCKS(10, "STARROCKS"),;
private Integer code; private Integer code;

View File

@@ -17,7 +17,20 @@ import java.util.List;
@Slf4j @Slf4j
public abstract class BaseDbAdaptor implements DbAdaptor { public abstract class BaseDbAdaptor implements DbAdaptor {
public List<String> getDBs(ConnectInfo connectionInfo) throws SQLException { @Override
public List<String> getCatalogs(ConnectInfo connectInfo) throws SQLException {
// Apart from supporting multiple catalog types of data sources, other types will return an
// empty set by default.
return List.of();
}
public List<String> getDBs(ConnectInfo connectionInfo, String catalog) throws SQLException {
// Except for special types implemented separately, the generic logic catalog does not take
// effect.
return getDBs(connectionInfo);
}
protected List<String> getDBs(ConnectInfo connectionInfo) throws SQLException {
List<String> dbs = Lists.newArrayList(); List<String> dbs = Lists.newArrayList();
DatabaseMetaData metaData = getDatabaseMetaData(connectionInfo); DatabaseMetaData metaData = getDatabaseMetaData(connectionInfo);
try { try {

View File

@@ -14,7 +14,9 @@ public interface DbAdaptor {
String rewriteSql(String sql); String rewriteSql(String sql);
List<String> getDBs(ConnectInfo connectInfo) throws SQLException; List<String> getCatalogs(ConnectInfo connectInfo) throws SQLException;
List<String> getDBs(ConnectInfo connectInfo, String catalog) throws SQLException;
List<String> getTables(ConnectInfo connectInfo, String schemaName) throws SQLException; List<String> getTables(ConnectInfo connectInfo, String schemaName) throws SQLException;

View File

@@ -18,6 +18,7 @@ public class DbAdaptorFactory {
dbAdaptorMap.put(EngineType.OTHER.getName(), new DefaultDbAdaptor()); dbAdaptorMap.put(EngineType.OTHER.getName(), new DefaultDbAdaptor());
dbAdaptorMap.put(EngineType.DUCKDB.getName(), new DuckdbAdaptor()); dbAdaptorMap.put(EngineType.DUCKDB.getName(), new DuckdbAdaptor());
dbAdaptorMap.put(EngineType.HANADB.getName(), new HanadbAdaptor()); dbAdaptorMap.put(EngineType.HANADB.getName(), new HanadbAdaptor());
dbAdaptorMap.put(EngineType.STARROCKS.getName(), new StarrocksAdaptor());
} }
public static DbAdaptor getEngineAdaptor(String engineType) { public static DbAdaptor getEngineAdaptor(String engineType) {

View File

@@ -0,0 +1,42 @@
package com.tencent.supersonic.headless.core.adaptor.db;
import com.google.common.collect.Lists;
import com.tencent.supersonic.headless.core.pojo.ConnectInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;
import java.sql.*;
import java.util.List;
@Slf4j
public class StarrocksAdaptor extends MysqlAdaptor {
@Override
public List<String> getCatalogs(ConnectInfo connectInfo) throws SQLException {
List<String> catalogs = Lists.newArrayList();
try (Connection con = DriverManager.getConnection(connectInfo.getUrl(),
connectInfo.getUserName(), connectInfo.getPassword());
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("SHOW CATALOGS")) {
while (rs.next()) {
catalogs.add(rs.getString(1));
}
}
return catalogs;
}
@Override
public List<String> getDBs(ConnectInfo connectionInfo, String catalog) throws SQLException {
Assert.hasText(catalog, "StarRocks type catalog can not be null or empty");
List<String> dbs = Lists.newArrayList();
try (Connection con = DriverManager.getConnection(connectionInfo.getUrl(),
connectionInfo.getUserName(), connectionInfo.getPassword());
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("SHOW DATABASES IN " + catalog)) {
while (rs.next()) {
dbs.add(rs.getString(1));
}
}
return dbs;
}
}

View File

@@ -16,6 +16,7 @@ public class DbParameterFactory {
parametersBuilder.put(EngineType.MYSQL.getName(), new MysqlParametersBuilder()); parametersBuilder.put(EngineType.MYSQL.getName(), new MysqlParametersBuilder());
parametersBuilder.put(EngineType.POSTGRESQL.getName(), new PostgresqlParametersBuilder()); parametersBuilder.put(EngineType.POSTGRESQL.getName(), new PostgresqlParametersBuilder());
parametersBuilder.put(EngineType.HANADB.getName(), new HanadbParametersBuilder()); parametersBuilder.put(EngineType.HANADB.getName(), new HanadbParametersBuilder());
parametersBuilder.put(EngineType.STARROCKS.getName(), new StarrocksParametersBuilder());
parametersBuilder.put(EngineType.OTHER.getName(), new OtherParametersBuilder()); parametersBuilder.put(EngineType.OTHER.getName(), new OtherParametersBuilder());
} }

View File

@@ -0,0 +1,16 @@
package com.tencent.supersonic.headless.server.pojo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@Slf4j
public class StarrocksParametersBuilder extends DefaultParametersBuilder {
@Override
public List<DatabaseParameter> build() {
return super.build();
}
}

View File

@@ -76,9 +76,15 @@ public class DatabaseController {
return databaseService.executeSql(sqlExecuteReq, user); return databaseService.executeSql(sqlExecuteReq, user);
} }
@RequestMapping("/getCatalogs")
public List<String> getCatalogs(@RequestParam("id") Long databaseId) throws SQLException {
return databaseService.getCatalogs(databaseId);
}
@RequestMapping("/getDbNames") @RequestMapping("/getDbNames")
public List<String> getDbNames(@RequestParam("id") Long databaseId) throws SQLException { public List<String> getDbNames(@RequestParam("id") Long databaseId,
return databaseService.getDbNames(databaseId); @RequestParam(value = "catalog", required = false) String catalog) throws SQLException {
return databaseService.getDbNames(databaseId, catalog);
} }
@RequestMapping("/getTables") @RequestMapping("/getTables")

View File

@@ -36,7 +36,9 @@ public interface DatabaseService {
void deleteDatabase(Long databaseId); void deleteDatabase(Long databaseId);
List<String> getDbNames(Long id) throws SQLException; List<String> getCatalogs(Long id) throws SQLException;
List<String> getDbNames(Long id, String catalog) throws SQLException;
List<String> getTables(Long id, String db) throws SQLException; List<String> getTables(Long id, String db) throws SQLException;

View File

@@ -200,10 +200,17 @@ public class DatabaseServiceImpl extends ServiceImpl<DatabaseDOMapper, DatabaseD
} }
@Override @Override
public List<String> getDbNames(Long id) throws SQLException { public List<String> getCatalogs(Long id) throws SQLException {
DatabaseResp databaseResp = getDatabase(id); DatabaseResp databaseResp = getDatabase(id);
DbAdaptor dbAdaptor = DbAdaptorFactory.getEngineAdaptor(databaseResp.getType()); DbAdaptor dbAdaptor = DbAdaptorFactory.getEngineAdaptor(databaseResp.getType());
return dbAdaptor.getDBs(DatabaseConverter.getConnectInfo(databaseResp)); return dbAdaptor.getCatalogs(DatabaseConverter.getConnectInfo(databaseResp));
}
@Override
public List<String> getDbNames(Long id, String catalog) throws SQLException {
DatabaseResp databaseResp = getDatabase(id);
DbAdaptor dbAdaptor = DbAdaptorFactory.getEngineAdaptor(databaseResp.getType());
return dbAdaptor.getDBs(DatabaseConverter.getConnectInfo(databaseResp), catalog);
} }
@Override @Override

View File

@@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Form, Input, Spin, Select, message } from 'antd'; import { Form, Input, Spin, Select, message } from 'antd';
import type { FormInstance } from 'antd/lib/form'; import type { FormInstance } from 'antd/lib/form';
import { getDbNames, getTables, getDimensionList } from '../../service'; import {getDbNames, getTables, getDimensionList, getCatalogs} from '../../service';
import { ISemantic } from '../../data'; import { ISemantic } from '../../data';
import FormItemTitle from '@/components/FormHelper/FormItemTitle'; import FormItemTitle from '@/components/FormHelper/FormItemTitle';
@@ -20,13 +20,16 @@ const ModelBasicForm: React.FC<Props> = ({
isEdit, isEdit,
modelItem, modelItem,
databaseConfigList, databaseConfigList,
form,
mode = 'normal', mode = 'normal',
}) => { }) => {
const [currentDbLinkConfigId, setCurrentDbLinkConfigId] = useState<number>(); const [currentDbLinkConfigId, setCurrentDbLinkConfigId] = useState<number>();
const [catalogList, setCatalogList] = useState<string[]>([]);
const [dbNameList, setDbNameList] = useState<string[]>([]); const [dbNameList, setDbNameList] = useState<string[]>([]);
const [tableNameList, setTableNameList] = useState<any[]>([]); const [tableNameList, setTableNameList] = useState<any[]>([]);
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
const [dimensionOptions, setDimensionOptions] = useState<{ label: string; value: number }[]>([]); const [dimensionOptions, setDimensionOptions] = useState<{ label: string; value: number }[]>([]);
const [catalogSelectOpen, setCatalogSelectOpen] = useState<boolean>(false);
useEffect(() => { useEffect(() => {
if (modelItem?.id) { if (modelItem?.id) {
@@ -50,9 +53,49 @@ const ModelBasicForm: React.FC<Props> = ({
} }
}; };
const queryDbNameList = async (databaseId: number) => { const onDatabaseSelect = (databaseId: number, type: string) => {
setLoading(true); setLoading(true);
const { code, data, msg } = await getDbNames(databaseId); if (type === 'STARROCKS') {
queryCatalogList(databaseId)
setCatalogSelectOpen(true);
setDbNameList([]);
} else {
queryDbNameList(databaseId, "");
setCatalogSelectOpen(false);
setCatalogList([]);
}
form.setFieldsValue({
catalog: undefined,
dbName: undefined,
tableName: undefined,
})
};
const queryCatalogList = async (databaseId: number) => {
setLoading(true);
const { code, data, msg } = await getCatalogs(databaseId);
setLoading(false)
if (code === 200) {
const list = data || [];
setCatalogList(list);
} else {
message.error(msg);
}
}
const onCatalogSelect = (catalog: string) => {
if (currentDbLinkConfigId) {
queryDbNameList(currentDbLinkConfigId, catalog);
}
form.setFieldsValue({
dbName: undefined,
tableName: undefined,
})
}
const queryDbNameList = async (databaseId: number, catalog: string) => {
setLoading(true);
const { code, data, msg } = await getDbNames(databaseId, catalog);
setLoading(false); setLoading(false);
if (code === 200) { if (code === 200) {
const list = data || []; const list = data || [];
@@ -61,6 +104,7 @@ const ModelBasicForm: React.FC<Props> = ({
message.error(msg); message.error(msg);
} }
}; };
const queryTableNameList = async (databaseName: string) => { const queryTableNameList = async (databaseName: string) => {
if (!currentDbLinkConfigId) { if (!currentDbLinkConfigId) {
return; return;
@@ -89,18 +133,37 @@ const ModelBasicForm: React.FC<Props> = ({
showSearch showSearch
placeholder="请选择数据库连接" placeholder="请选择数据库连接"
disabled={isEdit} disabled={isEdit}
onChange={(dbLinkConfigId: number) => { onSelect={(dbLinkConfigId: number, option) => {
queryDbNameList(dbLinkConfigId); onDatabaseSelect(dbLinkConfigId, option.type);
setCurrentDbLinkConfigId(dbLinkConfigId); setCurrentDbLinkConfigId(dbLinkConfigId);
}} }}
> >
{databaseConfigList.map((item) => ( {databaseConfigList.map((item) => (
<Select.Option key={item.id} value={item.id} disabled={!item.hasUsePermission}> <Select.Option key={item.id} value={item.id} disabled={!item.hasUsePermission} type={item.type}>
{item.name} {item.name}
</Select.Option> </Select.Option>
))} ))}
</Select> </Select>
</FormItem> </FormItem>
<FormItem
name="catalog"
label="Catalog"
rules={[{ required: true, message: '请选择Catalog' }]}
hidden={!catalogSelectOpen}
>
<Select
showSearch
placeholder="请选择Catalog"
disabled={isEdit}
onSelect={onCatalogSelect}
>
{catalogList.map((item) => (
<Select.Option key={item} value={item}>
{item}
</Select.Option>
))}
</Select>
</FormItem>
<FormItem <FormItem
name="dbName" name="dbName"
label="数据库名" label="数据库名"
@@ -110,8 +173,11 @@ const ModelBasicForm: React.FC<Props> = ({
showSearch showSearch
placeholder="请先选择一个数据库连接" placeholder="请先选择一个数据库连接"
disabled={isEdit} disabled={isEdit}
onChange={(dbName: string) => { onSelect={(dbName: string) => {
queryTableNameList(dbName); queryTableNameList(dbName);
form.setFieldsValue({
tableName: undefined,
})
}} }}
> >
{dbNameList.map((item) => ( {dbNameList.map((item) => (

View File

@@ -379,11 +379,21 @@ export async function listColumnsBySql(data: { databaseId: number; sql: string }
}); });
} }
export function getDbNames(dbId: number): Promise<any> { export function getCatalogs(dbId: number): Promise<any> {
return request(`${process.env.API_BASE_URL}database/getCatalogs`, {
method: 'GET',
params: {
id: dbId,
},
});
}
export function getDbNames(dbId: number, catalog: string): Promise<any> {
return request(`${process.env.API_BASE_URL}database/getDbNames`, { return request(`${process.env.API_BASE_URL}database/getDbNames`, {
method: 'GET', method: 'GET',
params: { params: {
id: dbId, id: dbId,
catalog: catalog,
}, },
}); });
} }