From 5f513eb838f819f8011e7d68a4933e25fd92fa96 Mon Sep 17 00:00:00 2001 From: fit2cloud-chenyw Date: Wed, 20 Aug 2025 14:26:46 +0800 Subject: [PATCH 1/2] perf: Failed to save assistant appearance settings --- backend/apps/system/api/assistant.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/apps/system/api/assistant.py b/backend/apps/system/api/assistant.py index bd93e5d9..a2b4dfc9 100644 --- a/backend/apps/system/api/assistant.py +++ b/backend/apps/system/api/assistant.py @@ -90,7 +90,8 @@ async def ui(session: SessionDep, data: str = Form(), files: List[UploadFile] = file.filename = file_name if flag_name == 'logo' or flag_name == 'float_icon': SQLBotFileUtils.check_file(file=file, file_types=[".jpg", ".jpeg", ".png", ".svg"], limit_file_size=(10 * 1024 * 1024)) - SQLBotFileUtils.detete_file(config_obj.get(flag_name)) + if config_obj.get(flag_name): + SQLBotFileUtils.detete_file(config_obj.get(flag_name)) file_id = await SQLBotFileUtils.upload(file) ui_schema_dict[flag_name] = file_id else: From 134087bb3f47d47d29093f73f86ea100c461f787 Mon Sep 17 00:00:00 2001 From: junjun Date: Wed, 20 Aug 2025 15:30:37 +0800 Subject: [PATCH 2/2] feat: support Apache Doris datasource --- backend/apps/datasource/crud/datasource.py | 2 +- backend/apps/db/constant.py | 1 + backend/apps/db/db.py | 49 ++++++++++++++++++ backend/apps/db/db_sql.py | 8 +-- backend/apps/db/type.py | 3 +- frontend/src/assets/datasource/icon_doris.png | Bin 0 -> 597 bytes frontend/src/views/ds/js/ds-type.ts | 3 ++ 7 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 frontend/src/assets/datasource/icon_doris.png diff --git a/backend/apps/datasource/crud/datasource.py b/backend/apps/datasource/crud/datasource.py index 47e72b05..a3f9b65f 100644 --- a/backend/apps/datasource/crud/datasource.py +++ b/backend/apps/datasource/crud/datasource.py @@ -267,7 +267,7 @@ def preview(session: SessionDep, current_user: CurrentUser, id: int, data: Table conf = DatasourceConf(**json.loads(aes_decrypt(ds.configuration))) if ds.type != "excel" else get_engine_config() sql: str = "" - if ds.type == "mysql": + if ds.type == "mysql" or ds.type == "doris": sql = f"""SELECT `{"`, `".join(fields)}` FROM `{data.table.table_name}` {where} LIMIT 100""" diff --git a/backend/apps/db/constant.py b/backend/apps/db/constant.py index dc4b5807..fc118d9d 100644 --- a/backend/apps/db/constant.py +++ b/backend/apps/db/constant.py @@ -20,6 +20,7 @@ class DB(Enum): oracle = ('oracle', '"', '"', ConnectType.sqlalchemy) ck = ('ck', '"', '"', ConnectType.sqlalchemy) dm = ('dm', '"', '"', ConnectType.py_driver) + doris = ('doris', '`', '`', ConnectType.py_driver) def __init__(self, type, prefix, suffix, connect_type: ConnectType): self.type = type diff --git a/backend/apps/db/db.py b/backend/apps/db/db.py index 69c44f8f..5081c2bb 100644 --- a/backend/apps/db/db.py +++ b/backend/apps/db/db.py @@ -8,6 +8,7 @@ from apps.db.db_sql import get_table_sql, get_field_sql if platform.system() != "Darwin": import dmPython +import pymysql from sqlalchemy import create_engine, text, Engine from sqlalchemy.orm import sessionmaker @@ -125,6 +126,19 @@ def check_connection(trans: Trans, ds: CoreDatasource, is_raise: bool = False): if is_raise: raise HTTPException(status_code=500, detail=trans('i18n_ds_invalid') + f': {e.args}') return False + elif ds.type == 'doris': + with pymysql.connect(user=conf.username, passwd=conf.password, host=conf.host, + port=conf.port, db=conf.database, connect_timeout=10, + read_timeout=10) as conn, conn.cursor() as cursor: + try: + cursor.execute('select 1') + SQLBotLogUtil.info("success") + return True + except Exception as e: + SQLBotLogUtil.error(f"Datasource {ds.id} connection failed: {e}") + if is_raise: + raise HTTPException(status_code=500, detail=trans('i18n_ds_invalid') + f': {e.args}') + return False def get_schema(ds: CoreDatasource): @@ -171,6 +185,14 @@ def get_tables(ds: CoreDatasource): res = cursor.fetchall() res_list = [TableSchema(*item) for item in res] return res_list + elif ds.type == 'doris': + with pymysql.connect(user=conf.username, passwd=conf.password, host=conf.host, + port=conf.port, db=conf.database, connect_timeout=conf.timeout, + read_timeout=conf.timeout) as conn, conn.cursor() as cursor: + cursor.execute(sql) + res = cursor.fetchall() + res_list = [TableSchema(*item) for item in res] + return res_list def get_fields(ds: CoreDatasource, table_name: str = None): @@ -191,6 +213,14 @@ def get_fields(ds: CoreDatasource, table_name: str = None): res = cursor.fetchall() res_list = [ColumnSchema(*item) for item in res] return res_list + elif ds.type == 'doris': + with pymysql.connect(user=conf.username, passwd=conf.password, host=conf.host, + port=conf.port, db=conf.database, connect_timeout=conf.timeout, + read_timeout=conf.timeout) as conn, conn.cursor() as cursor: + cursor.execute(sql) + res = cursor.fetchall() + res_list = [ColumnSchema(*item) for item in res] + return res_list def exec_sql(ds: CoreDatasource | AssistantOutDsSchema, sql: str, origin_column=False): @@ -233,3 +263,22 @@ def exec_sql(ds: CoreDatasource | AssistantOutDsSchema, sql: str, origin_column= "sql": bytes.decode(base64.b64encode(bytes(sql, 'utf-8')))} except Exception as ex: raise ex + elif ds.type == 'doris': + with pymysql.connect(user=conf.username, passwd=conf.password, host=conf.host, + port=conf.port, db=conf.database, connect_timeout=conf.timeout, + read_timeout=conf.timeout) as conn, conn.cursor() as cursor: + try: + cursor.execute(sql) + res = cursor.fetchall() + columns = [field[0] for field in cursor.description] if origin_column else [field[0].lower() for + field in + cursor.description] + result_list = [ + {str(columns[i]): float(value) if isinstance(value, Decimal) else value for i, value in + enumerate(tuple_item)} + for tuple_item in res + ] + return {"fields": columns, "data": result_list, + "sql": bytes.decode(base64.b64encode(bytes(sql, 'utf-8')))} + except Exception as ex: + raise ex diff --git a/backend/apps/db/db_sql.py b/backend/apps/db/db_sql.py index d7ba83a8..5b04a8dd 100644 --- a/backend/apps/db/db_sql.py +++ b/backend/apps/db/db_sql.py @@ -4,7 +4,7 @@ from apps.datasource.models.datasource import CoreDatasource, DatasourceConf def get_table_sql(ds: CoreDatasource, conf: DatasourceConf): - if ds.type == "mysql": + if ds.type == "mysql" or ds.type == "doris": return f""" SELECT TABLE_NAME, @@ -31,7 +31,7 @@ def get_table_sql(ds: CoreDatasource, conf: DatasourceConf): AND t.TABLE_SCHEMA = '{conf.dbSchema}' """ elif ds.type == "pg" or ds.type == "excel": - return """ + return f""" SELECT c.relname AS TABLE_NAME, COALESCE(d.description, obj_description(c.oid)) AS TABLE_COMMENT FROM pg_class c @@ -39,7 +39,7 @@ def get_table_sql(ds: CoreDatasource, conf: DatasourceConf): pg_namespace n ON n.oid = c.relnamespace LEFT JOIN pg_description d ON d.objoid = c.oid AND d.objsubid = 0 - WHERE n.nspname = current_schema() + WHERE n.nspname = '{conf.dbSchema}' AND c.relkind IN ('r', 'v', 'p', 'm') AND c.relname NOT LIKE 'pg_%' AND c.relname NOT LIKE 'sql_%' @@ -83,7 +83,7 @@ def get_table_sql(ds: CoreDatasource, conf: DatasourceConf): def get_field_sql(ds: CoreDatasource, conf: DatasourceConf, table_name: str = None): - if ds.type == "mysql": + if ds.type == "mysql" or ds.type == "doris": sql1 = f""" SELECT COLUMN_NAME, diff --git a/backend/apps/db/type.py b/backend/apps/db/type.py index d106a437..3a033898 100644 --- a/backend/apps/db/type.py +++ b/backend/apps/db/type.py @@ -11,5 +11,6 @@ def db_type_relation() -> Dict: "excel": "Excel/CSV", "oracle": "Oracle", "ck": "ClickHouse", - "dm": "达梦" + "dm": "达梦", + "doris": "Apache Doris" } diff --git a/frontend/src/assets/datasource/icon_doris.png b/frontend/src/assets/datasource/icon_doris.png new file mode 100644 index 0000000000000000000000000000000000000000..8a80e96f4c649147d71cff76bbae6a96a0589747 GIT binary patch literal 597 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaAy$bLNaRt&0GtZ=*nHhR@Rl@%H zGOLb-9$O`~`Hc7Br2)t1i?2ESW-lMWU-t2c23XvI1z-si0FJ zDKL3TbR`ghq<{>dhzN*WBC_g~*veBt!$FEO)&`wg1e6Lsy%@+41zHVO4ABVD1(XFV zgONZk7(rw~B*>7(|NsB@k6*kE=vl9lAirP+R#r9+PEIaC1w|EoBV!X&3kN4BXBSrw zPj4T;fPlcDknog>+GWc(ZrZYA*RI{W_v}4%^w`N$XRhD6|M<Vo1CfS@kvCd9W_N<7X#8BR1!S!Ux zMM3}lr`|kg_*%Gmzt@%bb=6a?7TlD#DLjyR%=Xgnxr?`7z4Ti*|K>Zk@7d>eeQUh7 qbj#aCU0LFh?cVG2mzMJUF_wSQW6jXMeN8yfI}Dz#elF{r5}E)Tb|V}B literal 0 HcmV?d00001 diff --git a/frontend/src/views/ds/js/ds-type.ts b/frontend/src/views/ds/js/ds-type.ts index ee0571d7..e1ce6c91 100644 --- a/frontend/src/views/ds/js/ds-type.ts +++ b/frontend/src/views/ds/js/ds-type.ts @@ -5,6 +5,7 @@ import pg from '@/assets/datasource/icon_PostgreSQL.png' import sqlServer from '@/assets/datasource/icon_SQL_Server.png' import ck from '@/assets/datasource/icon_ck.png' import dm from '@/assets/datasource/icon_dm.png' +import doris from '@/assets/datasource/icon_doris.png' import { i18n } from '@/i18n' const t = i18n.global.t @@ -16,6 +17,7 @@ export const dsType = [ { label: 'SQL Server', value: 'sqlServer' }, { label: 'ClickHouse', value: 'ck' }, { label: '达梦', value: 'dm' }, + { label: 'Apache Doris', value: 'doris' }, ] export const dsTypeWithImg = [ @@ -26,6 +28,7 @@ export const dsTypeWithImg = [ { name: 'SQL Server', type: 'sqlServer', img: sqlServer }, { name: 'ClickHouse', type: 'ck', img: ck }, { name: '达梦', type: 'dm', img: dm }, + { name: 'Apache Doris', type: 'doris', img: doris }, ] export const haveSchema = ['sqlServer', 'pg', 'oracle', 'dm']