merge mongo

This commit is contained in:
shanhexi
2023-11-26 23:46:04 +08:00
37 changed files with 737 additions and 460 deletions

View File

@ -25,9 +25,25 @@ export default defineConfig({
component: '@/layouts/GlobalLayout',
routes: [
{
path: '/',
path: '/connections',
component: 'main',
},
{
path: '/dashboard',
component: 'main',
},
{
path: '/team',
component: 'main',
},
{
path: '/workspace',
component: 'main',
},
{
path: '/',
redirect: '/connections',
},
],
},
],

View File

@ -10,7 +10,8 @@ interface IProps {
boundInfo: {
databaseName: string;
dataSourceId: number;
type: DatabaseTypeCode;
dataSourceName: string;
databaseType: DatabaseTypeCode;
schemaName?: string;
consoleId: number;
status: ConsoleStatus;
@ -20,18 +21,6 @@ interface IProps {
loadSQL: () => Promise<string>;
}
interface IContext {
boundInfoContext: {
databaseName: string;
dataSourceId: number;
type: DatabaseTypeCode;
schemaName?: string;
consoleId: number;
status: ConsoleStatus;
};
setBoundInfoContext: (boundInfo: IContext['boundInfoContext']) => void;
}
const SQLExecute = memo<IProps>((props) => {
const { boundInfo: _boundInfo, initDDL, loadSQL } = props;
const draggableRef = useRef<any>();

View File

@ -124,7 +124,8 @@ export const useGetRightClickMenu = (props: IProps) => {
createConsole({
name: 'create console',
dataSourceId: treeNodeData.extraParams!.dataSourceId!,
type: treeNodeData.extraParams!.databaseType!,
dataSourceName: treeNodeData.extraParams!.dataSourceName!,
databaseType: treeNodeData.extraParams!.databaseType!,
databaseName: treeNodeData.extraParams?.databaseName,
schemaName: treeNodeData.extraParams?.schemaName,
});

View File

@ -125,7 +125,7 @@ export const treeConfig: { [key in TreeNodeType]: ITreeConfigItem } = {
const _extraParams = params.extraParams;
delete params.extraParams;
connectionService
.getDBList(params)
.getDatabaseList(params)
.then((res) => {
const data: ITreeNode[] = res.map((t: any) => {
return {

View File

@ -102,7 +102,7 @@ function CascaderDB(props: IProps) {
if (curDataSourceId === undefined) {
return;
}
const databaseList = await connection.getDBList({
const databaseList = await connection.getDatabaseList({
dataSourceId: curDataSourceId,
});

View File

@ -30,6 +30,7 @@
}
.downloadTextError{
color: var(--color-error-text);
cursor: pointer;
}
.downloadTextLoading{
display: flex;

View File

@ -29,7 +29,7 @@ const OperationLine = (props: IProps) => {
sql = editorRef?.current?.getAllContent() || '';
setValueType = 'cover';
}
formatSql(sql, boundInfo.type!).then((res) => {
formatSql(sql, boundInfo.databaseType!).then((res) => {
editorRef?.current?.setValue(res, setValueType);
});
};
@ -54,10 +54,8 @@ const OperationLine = (props: IProps) => {
{i18n('common.button.format')}
</Button>
</div>
<div>
<SelectBoundInfo setBoundInfo={setBoundInfo} boundInfo={boundInfo} />
</div>
</div>
);
};

View File

@ -1,3 +1,19 @@
.consoleOptionsRight {
display: flex;
}
.boundInfoBox {
display: flex;
align-items: center;
padding: 5px 6px 5px 8px;
border-radius: 4px;
cursor: pointer;
.boundInfoName {
margin: 0px 6px 0px 4px;
}
&:hover {
background-color: var(--color-hover-bg);
}
}

View File

@ -1,8 +1,11 @@
import React, { useEffect, useMemo, useState } from 'react';
import React, { useEffect, useMemo, useState, memo } from 'react';
import { IBoundInfo } from '../../index';
import { Dropdown } from 'antd';
import { useConnectionStore } from '@/pages/main/store/connection';
import connectionService from '@/service/connection';
import historyService from '@/service/history';
import Iconfont from '@/components/Iconfont';
import { databaseMap } from '@/constants/database';
import styles from './index.less';
import {
@ -23,7 +26,8 @@ interface IOption<T> {
value: T;
}
const SelectBoundInfo = (props: IProps) => {
const SelectBoundInfo = memo(
(props: IProps) => {
const { boundInfo, setBoundInfo } = props;
const connectionList = useConnectionStore((state) => state.connectionList);
const [databaseNameList, setDatabaseNameList] = useState<IOption<string>[]>();
@ -35,23 +39,28 @@ const SelectBoundInfo = (props: IProps) => {
label: item.alias,
value: item.id,
type: item.type,
}));
})) || [];
}, [connectionList]);
// 编辑器绑定的数据库类型变化时,重新注册智能提示
useEffect(() => {
registerIntelliSenseKeyword(boundInfo.type);
registerIntelliSenseKeyword(boundInfo.databaseType);
}, [boundInfo.dataSourceId]);
// 编辑器绑定的数据库类型变化时,重新注册智能提示
useEffect(() => {}, [boundInfo.dataSourceId]);
// 当数据源变化时,重新获取数据库列表
useEffect(() => {
if (boundInfo.dataSourceId === null || boundInfo.dataSourceId === undefined) {
return;
}
getDatabaseList();
}, [boundInfo.dataSourceId]);
// 当数据库名变化时重新获取schema列表
useEffect(() => {
getSchemaList();
}, [boundInfo.databaseName]);
// 获取数据库列表
const getDatabaseList = () => {
connectionService
.getDBList({
.getDatabaseList({
dataSourceId: boundInfo.dataSourceId,
})
.then((res) => {
@ -73,8 +82,9 @@ const SelectBoundInfo = (props: IProps) => {
setDatabaseNameList(_databaseNameList);
registerIntelliSenseDatabase(editorDatabaseTips);
});
}, [boundInfo.dataSourceId]);
}
// 获取schema列表
const getSchemaList = () => {
if (boundInfo.databaseName === null || boundInfo.databaseName === undefined) {
return;
@ -95,39 +105,50 @@ const SelectBoundInfo = (props: IProps) => {
});
};
useEffect(() => {
getSchemaList();
}, [boundInfo.databaseName]);
// 选择数据源
const changeDataSource = (item) => {
const curData = dataSourceList?.find((i) => i.key === item.key);
const currentData = dataSourceList.find((i) => i.key === item.key)!;
setBoundInfo({
...boundInfo,
dataSourceId: curData?.value,
dataSourceName: curData?.label,
type: curData?.type,
dataSourceId: currentData.value,
dataSourceName: currentData.label,
databaseType: currentData.type,
databaseName: void 0,
schemaName: void 0,
});
};
// 选择数据库
const changeDataBase = (item) => {
const _databaseName = databaseNameList?.find((i) => i.key === item.key)?.value;
setBoundInfo({
...boundInfo,
databaseName: _databaseName,
schemaName: void 0,
});
historyService.updateSavedConsole({
id: boundInfo.consoleId,
databaseName: _databaseName,
})
};
// 选择schema
const changeSchema = (item) => {
const _schemaName = schemaList?.find((i) => i.key === item.key)?.value;
setBoundInfo({
...boundInfo,
schemaName: _schemaName,
});
historyService.updateSavedConsole({
id: boundInfo.consoleId,
schemaName: _schemaName,
})
};
console.log(boundInfo)
return (
<div className={styles.consoleOptionsRight}>
<Dropdown
@ -135,8 +156,13 @@ const SelectBoundInfo = (props: IProps) => {
items: dataSourceList,
onClick: changeDataSource,
}}
trigger={['click']}
>
<div>{boundInfo.dataSourceName || '请选择数据库'}</div>
<div className={styles.boundInfoBox}>
<Iconfont code={databaseMap[boundInfo.databaseType!]?.icon} />
<div className={styles.boundInfoName}>{boundInfo.dataSourceName || `<${'dataSource'}>`}</div>
<Iconfont code="&#x100be;" />
</div>
</Dropdown>
{boundInfo.dataSourceId && !!databaseNameList?.length && (
@ -145,22 +171,34 @@ const SelectBoundInfo = (props: IProps) => {
items: databaseNameList,
onClick: changeDataBase,
}}
trigger={['click']}
>
<div>{boundInfo.databaseName || '请选择数据库'}</div>
<div className={styles.boundInfoBox}>
<Iconfont code="&#xe669;" />
<div className={styles.boundInfoName}>{boundInfo.databaseName || `<${'database'}>`}</div>
<Iconfont code="&#x100be;" />
</div>
</Dropdown>
)}
{boundInfo.databaseName && !!schemaList?.length && (
<Dropdown
menu={{
items: schemaList,
onClick: changeSchema,
}}
trigger={['click']}
>
<div>{boundInfo.schemaName || '请选择schema'}</div>
<div className={styles.boundInfoBox}>
<Iconfont code="&#xe663;" />
<div className={styles.boundInfoName}>{boundInfo.schemaName || `<${'schema'}>`}</div>
<Iconfont code="&#x100be;" />
</div>
</Dropdown>
)}
</div>
);
};
}
)
export default SelectBoundInfo;

View File

@ -38,9 +38,9 @@ export type IAppendValue = {
export interface IBoundInfo {
consoleId: number;
consoleName: string;
dataSourceId?: number;
dataSourceName?: string;
type?: DatabaseTypeCode;
dataSourceId: number;
dataSourceName: string;
databaseType: DatabaseTypeCode;
databaseName?: string;
schemaName?: string;
status?: ConsoleStatus;
@ -421,14 +421,12 @@ function Console(props: IProps, ref: ForwardedRef<IConsoleRef>) {
ref={editorRef as any}
className={hasAiChat ? styles.consoleEditorWithChat : styles.consoleEditor}
addAction={addAction}
databaseType={boundInfo.type}
// databaseType={boundInfo.databaseType}
onSave={saveConsole}
onExecute={executeSQL}
options={props.editorOptions}
/>
{/* <NewEditor id={uid} dataSource={props.boundInfo.type} database={props.boundInfo.databaseName} /> */}
<Drawer
open={isAiDrawerOpen}
getContainer={false}

View File

@ -97,13 +97,6 @@ export const databaseMap: {
// port: 2883,
icon: '\ue982',
},
// [DatabaseTypeCode.REDIS]: {
// name: 'Redis',
// img: moreDBLogo,
// code: DatabaseTypeCode.REDIS,
// // port: 6379,
// icon: '\ue6a2',
// },
[DatabaseTypeCode.HIVE]: {
name: 'Hive',
img: moreDBLogo,
@ -118,13 +111,20 @@ export const databaseMap: {
// port: 54321,
icon: '\ue6a0',
},
// [DatabaseTypeCode.MONGODB]: {
// name: 'MongoDB',
// img: moreDBLogo,
// code: DatabaseTypeCode.MONGODB,
// // port: 27017,
// icon: '\uec21',
// },
[DatabaseTypeCode.MONGODB]: {
name: 'MongoDB',
img: moreDBLogo,
code: DatabaseTypeCode.MONGODB,
// port: 27017,
icon: '\uec21',
},
[DatabaseTypeCode.REDIS]: {
name: 'Redis',
img: moreDBLogo,
code: DatabaseTypeCode.REDIS,
// port: 6379,
icon: '\ue6a2',
},
};
export const databaseTypeList = Object.keys(databaseMap).map((keys) => {

View File

@ -0,0 +1,36 @@
import connectionService from '@/service/connection';
import { setConnectionEnvList, getConnectionList } from '@/pages/main/store/connection';
import { useWorkspaceStore } from '@/pages/main/workspace/store';
import { setCurrentConnectionDetails } from '@/pages/main/workspace/store/common';
const getConnection = () => {
const currentConnectionDetails = useWorkspaceStore.getState().currentConnectionDetails;
const getConnectionEnvList = () => {
connectionService.getEnvList().then((res) => {
setConnectionEnvList(res);
});
};
getConnectionList().then((res) => {
// 如果连接列表为空,则设置当前连接为空
if(res.length === 0){
setCurrentConnectionDetails(null);
return;
}
// 如果当前连接不存在,则设置当前连接为第一个连接
if (!currentConnectionDetails?.id) {
setCurrentConnectionDetails(res[0]);
return;
}
// 如果存在但是不在列表中,则设置当前连接为第一个连接
const currentConnection = res.find((item) => item.id === currentConnectionDetails?.id);
if (!currentConnection) {
setCurrentConnectionDetails(res[0]);
}
});
getConnectionEnvList();
};
export default getConnection;

View File

@ -10,7 +10,7 @@ interface ICreateConsoleParams {
dataSourceName?: string;
databaseName?: string;
schemaName?: string;
type?: DatabaseTypeCode;
databaseType?: DatabaseTypeCode;
operationType?: string;
}
@ -29,7 +29,7 @@ function useCreateConsole() {
status: ConsoleStatus.DRAFT,
tabOpened: ConsoleOpenedStatus.IS_OPEN,
operationType: WorkspaceTabType.CONSOLE,
type: params.type,
databaseType: params.databaseType,
dataSourceId: params.dataSourceId,
dataSourceName: params.dataSourceName,
};

View File

@ -1,50 +0,0 @@
import { useEffect } from 'react';
import connectionService from '@/service/connection';
import { setConnectionEnvList, getConnectionList } from '@/pages/main/store/connection';
import { useWorkspaceStore } from '@/pages/main/workspace/store';
import { setCurrentConnectionDetails } from '@/pages/main/workspace/store/common';
const useGetConnection = () => {
const { currentConnectionDetails } = useWorkspaceStore((state) => {
return {
currentConnectionDetails: state.currentConnectionDetails,
};
});
const getConnectionEnvList = () => {
connectionService.getEnvList().then((res) => {
setConnectionEnvList(res);
});
};
// 获取连接列表,获取连接环境列表
useEffect(() => {
getConnectionList().then((res) => {
// 如果连接列表为空,则设置当前连接为空
if(res.length === 0){
setCurrentConnectionDetails(null);
return;
}
// 如果当前连接不存在,则设置当前连接为第一个连接
if (!currentConnectionDetails?.id) {
setCurrentConnectionDetails(res[0]);
return;
}
// 如果存在但是不在列表中,则设置当前连接为第一个连接
const currentConnection = res.find((item) => item.id === currentConnectionDetails?.id);
if (!currentConnection) {
setCurrentConnectionDetails(res[0]);
}
});
getConnectionEnvList();
}, []);
return {
getConnectionList,
};
};
export default useGetConnection;

View File

@ -1,6 +1,5 @@
import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { connect } from 'umi';
import { Dropdown, Tooltip } from 'antd';
import classnames from 'classnames';
@ -8,17 +7,12 @@ import Iconfont from '@/components/Iconfont';
import BrandLogo from '@/components/BrandLogo';
import i18n from '@/i18n';
import { findObjListValue } from '@/utils';
import { getUser, userLogout } from '@/service/user';
import { INavItem } from '@/typings/main';
import { ILoginUser, IRole } from '@/typings/user';
// ----- model -----
import { IMainPageType } from '@/models/mainPage';
import { IWorkspaceModelType } from '@/models/workspace';
// ----- hooks -----
import useGetConnection from '@/hooks/useGetConnection';
import getConnection from '@/hooks/getConnection';
// ----- block -----
import Workspace from './workspace';
@ -27,10 +21,9 @@ import Connection from './connection';
import Team from './team';
import Setting from '@/blocks/Setting';
import { getUrlParam, updateQueryStringParameter } from '@/utils/url';
import styles from './index.less';
const navConfig: INavItem[] = [
const initNavConfig: INavItem[] = [
{
key: 'workspace',
icon: '\ue616',
@ -65,86 +58,56 @@ const navConfig: INavItem[] = [
},
];
interface IProps {
mainModel: IMainPageType['state'];
workspaceModel: IWorkspaceModelType['state'];
dispatch: any;
}
function MainPage(props: IProps) {
function MainPage() {
const navigate = useNavigate();
const { mainModel, dispatch } = props;
const { curPage } = mainModel;
const [activeNav, setActiveNav] = useState<INavItem | null>(null);
const [navConfig, setNavConfig] = useState<INavItem[]>(initNavConfig);
const [userInfo, setUserInfo] = useState<ILoginUser>();
// 获取当前连接
useGetConnection();
const [activeNavKey, setActiveNavKey] = useState<string>(window.location.pathname.split('/')[1]);
useEffect(() => {
handleInitPage();
getConnection();
}, []);
// 切换tab
useEffect(() => {
if (!activeNav) {
return;
// 获取当前地址栏的tab
const activeIndex = navConfig.findIndex((t) => `${t.key}` === activeNavKey);
if (activeIndex > -1) {
navConfig[activeIndex].isLoad = true;
setNavConfig([...navConfig]);
// 桌面端跳转这里应该要换TODO:
const href = window.location.origin + '/' + activeNavKey;
window.history.pushState({}, '', href);
}
}, [activeNavKey]);
activeNav.isLoad = true;
dispatch({
type: 'mainPage/updateCurPage',
payload: activeNav.key,
});
}, [activeNav]);
useEffect(() => {
// 全局状态curPage发生变化activeNav 需要同步变化
if (curPage && curPage !== activeNav?.key) {
const newActiveNav = navConfig[findObjListValue(navConfig, 'key', curPage)];
setActiveNav(newActiveNav);
}
localStorage.setItem('curPage', curPage);
}, [curPage]);
// 这里如果社区版没有登陆可能需要后端来个重定向?
const handleInitPage = async () => {
const cloneNavConfig = [...navConfig];
const res = await getUser();
if (res) {
setUserInfo(res);
const hasTeamIcon = navConfig.find((i) => i.key === 'team');
const hasTeamIcon = cloneNavConfig.find((i) => i.key === 'team');
if (res.admin && !hasTeamIcon) {
navConfig.splice(3, 0, {
cloneNavConfig.splice(3, 0, {
key: 'team',
icon: '\ue64b',
iconFontSize: 24,
isLoad: false,
isLoad: activeNavKey === 'team', // 如果当前是team直接加载
component: <Team />,
name: i18n('team.title'),
});
if (localStorage.getItem('curPage') === 'team') {
setActiveNav(navConfig[3]);
}
}
if (!res.admin && hasTeamIcon) {
navConfig.splice(3, 1);
if (localStorage.getItem('curPage') === 'team') {
setActiveNav(navConfig[2]);
cloneNavConfig.splice(3, 1);
}
}
}
const initPage = localStorage.getItem('curPage');
const initPageIndex = navConfig.findIndex((t) => `${t.key}` === initPage);
const activeIndex = initPageIndex > -1 ? initPageIndex : 2;
navConfig[activeIndex].isLoad = true;
setActiveNav(navConfig[activeIndex]);
setNavConfig([...cloneNavConfig]);
};
const switchingNav = (item: INavItem) => {
if (item.openBrowser) {
window.open(item.openBrowser, '_blank');
} else {
setActiveNav(item);
}
const switchingNav = (key: string) => {
setActiveNavKey(key);
};
const handleLogout = () => {
@ -190,15 +153,11 @@ function MainPage(props: IProps) {
<Tooltip key={item.key} placement="right" title={item.name}>
<li
className={classnames({
[styles.activeNav]: item.key == activeNav?.key,
[styles.activeNav]: item.key == activeNavKey,
})}
onClick={() => switchingNav(item)}
onClick={() => switchingNav(item.key)}
>
<Iconfont
style={{ '--icon-size': item.iconFontSize + 'px' } as any}
className={styles.icon}
code={item.icon}
/>
<Iconfont size={item.iconFontSize} className={styles.icon} code={item.icon} />
</li>
</Tooltip>
);
@ -214,7 +173,7 @@ function MainPage(props: IProps) {
<div className={styles.layoutRight}>
{navConfig.map((item) => {
return (
<div key={item.key} className={styles.componentBox} hidden={activeNav?.key !== item.key}>
<div key={item.key} className={styles.componentBox} hidden={activeNavKey !== item.key}>
{item.isLoad ? item.component : null}
</div>
);
@ -224,15 +183,4 @@ function MainPage(props: IProps) {
);
}
export default connect(
({
mainPage,
workspace,
}: {
mainPage: IMainPageType;
workspace: IWorkspaceModelType;
}) => ({
mainModel: mainPage,
workspaceModel: workspace,
}),
)(MainPage);
export default MainPage;

View File

@ -0,0 +1,32 @@
import { UseBoundStoreWithEqualityFn, createWithEqualityFn } from 'zustand/traditional';
import { devtools, persist } from 'zustand/middleware';
import { shallow } from 'zustand/shallow';
import { StoreApi } from 'zustand';
export interface IMainStore {
mainPageActiveTab: string;
}
const initMainStore = {
mainPageActiveTab: 'connections',
};
export const useMainStore: UseBoundStoreWithEqualityFn<StoreApi<IMainStore>> = createWithEqualityFn(
devtools(
persist(() => initMainStore, {
name: 'main-page-store',
getStorage: () => localStorage,
// 工作区的状态只保存 layout布局信息
partialize: (state: IMainStore) => ({
mainPageActiveTab: state.mainPageActiveTab,
}),
}),
),
shallow,
);
export const setMainPageActiveTab = (mainPageActiveTab: string) => {
return useMainStore.setState({
mainPageActiveTab,
});
};

View File

@ -32,11 +32,11 @@ export default memo<IProps>((props) => {
treeConfig['dataSource']
.getChildren?.({
dataSourceId: currentConnectionDetails.id,
dataSourceName: currentConnectionDetails.name,
dataSourceName: currentConnectionDetails.alias,
refresh: refresh,
extraParams: {
dataSourceId: currentConnectionDetails.id,
dataSourceName: currentConnectionDetails.name,
dataSourceName: currentConnectionDetails.alias,
databaseType: currentConnectionDetails.type,
},
})

View File

@ -43,7 +43,7 @@ const WorkspaceRight = memo<IProps>((props: IProps) => {
// 活跃的TabID
const [activeConsoleId, setActiveConsoleId] = useState<number | string | null>(null);
// 工作台tab列表
const [workspaceTabList, setWorkspaceTabList] = useState<IWorkspaceTab[]>([]);
const [workspaceTabList, setWorkspaceTabL] = useState<IWorkspaceTab[]>([]);
const { curWorkspaceParams, doubleClickTreeNodeData, createTabIntro, openConsoleList, createConsoleIntro } =
workspaceModel;
@ -53,7 +53,7 @@ const WorkspaceRight = memo<IProps>((props: IProps) => {
useEffect(() => {
setActiveConsoleId(null);
setWorkspaceTabList([]);
setWorkspaceTabL([]);
}, [curWorkspaceParams]);
// 根据保存的console列表生成tab列表
@ -81,9 +81,9 @@ const WorkspaceRight = memo<IProps>((props: IProps) => {
newAddList.push(t);
}
});
setWorkspaceTabList([...newWorkspaceTabList, ...newAddList]);
setWorkspaceTabL([...newWorkspaceTabList, ...newAddList]);
} else {
setWorkspaceTabList(newTabList || []);
setWorkspaceTabL(newTabList || []);
}
if (!activeConsoleId) {
setActiveConsoleId(newTabList[0]?.id);
@ -113,7 +113,7 @@ const WorkspaceRight = memo<IProps>((props: IProps) => {
useEffect(() => {
if (createConsoleIntro) {
if (workspaceTabList.findIndex((t) => t.id === createConsoleIntro.id) === -1) {
setWorkspaceTabList([...workspaceTabList, createConsoleIntro]);
setWorkspaceTabL([...workspaceTabList, createConsoleIntro]);
}
setActiveConsoleId(createConsoleIntro.id);
}
@ -145,7 +145,7 @@ const WorkspaceRight = memo<IProps>((props: IProps) => {
tableName: createTabIntro.treeNodeData.name,
},
};
setWorkspaceTabList([...workspaceTabList, newData]);
setWorkspaceTabL([...workspaceTabList, newData]);
setActiveConsoleId(id);
// 用完之后就清掉createTabIntro
@ -188,7 +188,7 @@ const WorkspaceRight = memo<IProps>((props: IProps) => {
}
return t;
});
setWorkspaceTabList(newList || []);
setWorkspaceTabL(newList || []);
});
};
const name = doubleClickTreeNodeData.name;
@ -227,7 +227,7 @@ const WorkspaceRight = memo<IProps>((props: IProps) => {
}
return t;
});
setWorkspaceTabList(newList || []);
setWorkspaceTabL(newList || []);
});
};
createConsole({
@ -264,7 +264,7 @@ const WorkspaceRight = memo<IProps>((props: IProps) => {
}
return t;
});
setWorkspaceTabList(newList || []);
setWorkspaceTabL(newList || []);
});
};
createConsole({
@ -301,7 +301,7 @@ const WorkspaceRight = memo<IProps>((props: IProps) => {
}
return t;
});
setWorkspaceTabList(newList || []);
setWorkspaceTabL(newList || []);
});
};
createConsole({
@ -327,7 +327,7 @@ const WorkspaceRight = memo<IProps>((props: IProps) => {
}
});
if (!flag) {
setWorkspaceTabList([
setWorkspaceTabL([
...(workspaceTabList || []),
{
id,
@ -434,7 +434,7 @@ const WorkspaceRight = memo<IProps>((props: IProps) => {
// uniqueData: newConsole,
// },
// ];
// setWorkspaceTabList(newList);
// setWorkspaceTabL(newList);
// callback?.(res, newList);
// setActiveConsoleId(res);
// });
@ -469,7 +469,7 @@ const WorkspaceRight = memo<IProps>((props: IProps) => {
// 删除 新增tab
// const onEdit = (action: 'add' | 'remove', data: ITabItem[]) => {
// if (action === 'remove') {
// setWorkspaceTabList(
// setWorkspaceTabL(
// workspaceTabList.filter((t) => {
// return data.findIndex((item) => item.key === t.id) === -1;
// }),
@ -515,7 +515,7 @@ const WorkspaceRight = memo<IProps>((props: IProps) => {
// uniqueData: newConsole,
// },
// ];
// setWorkspaceTabList(newList);
// setWorkspaceTabL(newList);
// setActiveConsoleId(res);
// });
// };
@ -583,7 +583,7 @@ const WorkspaceRight = memo<IProps>((props: IProps) => {
}
return t;
});
setWorkspaceTabList(list);
setWorkspaceTabL(list);
};
const tabsList = useMemo(() => {

View File

@ -57,6 +57,7 @@ const WorkspaceTabs = memo(() => {
title: item.name,
uniqueData: {
dataSourceId: item.dataSourceId,
dataSourceName: item.dataSourceName,
databaseType: item.type,
databaseName: item.databaseName,
schemaName: item.schemaName,
@ -83,7 +84,7 @@ const WorkspaceTabs = memo(() => {
createConsole({
dataSourceId: currentConnectionDetails?.id,
dataSourceName: currentConnectionDetails?.alias,
type: currentConnectionDetails?.type,
databaseType: currentConnectionDetails?.type,
});
};
@ -140,8 +141,9 @@ const WorkspaceTabs = memo(() => {
<SQLExecute
boundInfo={{
dataSourceId: uniqueData.dataSourceId,
dataSourceName: uniqueData.dataSourceName,
databaseName: uniqueData?.databaseName,
type: uniqueData.databaseType,
databaseType: uniqueData.databaseType,
schemaName: uniqueData?.schemaName,
consoleId: item.id as number,
status: uniqueData.status,

View File

@ -42,6 +42,7 @@ export const createConsole = (params: ICreateConsoleParams)=>{
ddl: params.ddl || '',
status: ConsoleStatus.DRAFT,
operationType: WorkspaceTabType.CONSOLE,
type: params.databaseType
};
return new Promise((resolve) => {

View File

@ -31,8 +31,8 @@ export const useWorkspaceStore: UseBoundStoreWithEqualityFn<StoreApi<IStore>> =
},
),
{
name: "workspaceStore"
}
name: 'workspaceStore',
},
),
shallow
shallow,
);

View File

@ -55,7 +55,7 @@ const remove = createRequest<{ id: number }, void>('/api/connection/datasource/:
const clone = createRequest<{ id: number }, void>('/api/connection/datasource/clone', { method: 'post' });
const getDBList = createRequest<{ dataSourceId: number; refresh?: boolean }, any>('/api/rdb/database/list', {
const getDatabaseList = createRequest<{ dataSourceId: number; refresh?: boolean }, any>('/api/rdb/database/list', {
method: 'get',
});
@ -69,7 +69,6 @@ const getDriverList = createRequest<IDriverParams, IDriverResponse>('/api/jdbc/d
method: 'get',
});
const downloadDriver = createRequest<{ dbType: string }, void>('/api/jdbc/driver/download', {
errorLevel: false,
method: 'get',
});
@ -96,7 +95,7 @@ export default {
update,
remove,
clone,
getDBList,
getDatabaseList,
getSchemaList,
close,
testSSH,

View File

@ -59,9 +59,15 @@
// }
// }
.ant-cascader-dropdown .ant-cascader-menu {
.ant-dropdown{
.ant-dropdown-menu {
height: auto;
max-height: 80vh;
max-height: 50vh;
// overflow: hidden ;
// &:hover {
// }
overflow: auto;
}
}
.ant-btn-link {

View File

@ -4,7 +4,8 @@ export interface ICreateConsoleParams {
name?: string;
ddl?: string;
dataSourceId: number;
type: DatabaseTypeCode;
dataSourceName: string;
databaseType: DatabaseTypeCode;
databaseName?: string;
schemaName?: string;
operationType?: string;

View File

@ -3,7 +3,7 @@ import { TreeNodeType, DatabaseTypeCode } from '@/constants';
export interface IExtraParams {
dataSourceId: number;
databaseType: DatabaseTypeCode;
dataSourceName?: string;
dataSourceName: string;
databaseName?: string;
schemaName?: string;
tableName?: string;

View File

@ -1,8 +1,38 @@
package ai.chat2db.plugin.mongodb;
import java.sql.Connection;
import java.sql.SQLException;
import ai.chat2db.spi.DBManage;
import ai.chat2db.spi.jdbc.DefaultDBManage;
import ai.chat2db.spi.sql.Chat2DBContext;
import ai.chat2db.spi.sql.ConnectInfo;
import ai.chat2db.spi.sql.SQLExecutor;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.util.StringUtils;
public class MongodbManage extends DefaultDBManage implements DBManage {
@Override
public void connectDatabase(Connection connection, String database) {
ConnectInfo connectInfo = Chat2DBContext.getConnectInfo();
if (ObjectUtils.anyNull(connectInfo) || StringUtils.isEmpty(connectInfo.getSchemaName())) {
return;
}
String schemaName = connectInfo.getSchemaName();
if (StringUtils.isEmpty(schemaName)) {
return;
}
try {
SQLExecutor.getInstance().execute(connection, "use " + schemaName + ";");
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void dropTable(Connection connection, String databaseName, String schemaName, String tableName) {
String sql = " db. " + tableName + ".drop();";
SQLExecutor.getInstance().executeSql(connection, sql, resultSet -> null);
}
}

View File

@ -2,18 +2,14 @@ package ai.chat2db.server.domain.core.impl;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import ai.chat2db.server.domain.api.param.*;
import ai.chat2db.server.domain.api.param.operation.OperationLogCreateParam;
import ai.chat2db.server.domain.api.service.OperationLogService;
import ai.chat2db.server.domain.api.service.TableService;
import ai.chat2db.server.domain.core.util.MetaNameUtils;
import ai.chat2db.spi.MetaData;
import ai.chat2db.spi.ValueHandler;
import ai.chat2db.spi.model.*;
import ai.chat2db.spi.sql.ConnectInfo;
import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.PagerUtils;
import com.alibaba.druid.sql.SQLUtils;
@ -21,16 +17,34 @@ import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
import com.alibaba.druid.sql.parser.ParserException;
import ai.chat2db.server.domain.api.param.DlCountParam;
import ai.chat2db.server.domain.api.param.DlExecuteParam;
import ai.chat2db.server.domain.api.param.SelectResultOperation;
import ai.chat2db.server.domain.api.param.TableQueryParam;
import ai.chat2db.server.domain.api.param.UpdateSelectResultParam;
import ai.chat2db.server.domain.api.param.operation.OperationLogCreateParam;
import ai.chat2db.server.domain.api.service.DlTemplateService;
import ai.chat2db.server.domain.api.service.OperationLogService;
import ai.chat2db.server.domain.api.service.TableService;
import ai.chat2db.server.domain.core.util.MetaNameUtils;
import ai.chat2db.server.tools.base.constant.EasyToolsConstant;
import ai.chat2db.server.tools.base.enums.DataSourceTypeEnum;
import ai.chat2db.server.tools.base.excption.BusinessException;
import ai.chat2db.server.tools.base.wrapper.result.DataResult;
import ai.chat2db.server.tools.base.wrapper.result.ListResult;
import ai.chat2db.server.tools.common.util.EasyCollectionUtils;
import ai.chat2db.server.tools.common.util.I18nUtils;
import ai.chat2db.spi.MetaData;
import ai.chat2db.spi.ValueHandler;
import ai.chat2db.spi.enums.DataTypeEnum;
import ai.chat2db.spi.enums.SqlTypeEnum;
import ai.chat2db.spi.model.ExecuteResult;
import ai.chat2db.spi.model.Header;
import ai.chat2db.spi.model.TableColumn;
import ai.chat2db.spi.model.TableIndex;
import ai.chat2db.spi.model.TableIndexColumn;
import ai.chat2db.spi.sql.Chat2DBContext;
import ai.chat2db.spi.sql.ConnectInfo;
import ai.chat2db.spi.sql.SQLExecutor;
import ai.chat2db.spi.util.JdbcUtils;
import ai.chat2db.spi.util.SqlUtils;
@ -71,7 +85,6 @@ public class DlTemplateServiceImpl implements DlTemplateService {
List<String> sqlList = SqlUtils.parse(param.getSql(), dbType);
if (CollectionUtils.isEmpty(sqlList)) {
throw new BusinessException("dataSource.sqlAnalysisError");
}
@ -127,29 +140,34 @@ public class DlTemplateServiceImpl implements DlTemplateService {
param.setSql(sql);
}
private ExecuteResult executeSQL(String originalSql, DbType dbType, DlExecuteParam param) {
int pageNo = 1;
int pageSize = 0;
Integer offset = null;
Integer count = null;
String sqlType = SqlTypeEnum.UNKNOWN.getCode();
// 解析sql
String type = Chat2DBContext.getConnectInfo().getDbType();
boolean supportDruid = !DataSourceTypeEnum.MONGODB.getCode().equals(type);
// 解析sql分页
SQLStatement sqlStatement;
SQLStatement sqlStatement = null;
if (supportDruid) {
try {
sqlStatement = SQLUtils.parseSingleStatement(originalSql, dbType);
// 是否需要代码帮忙分页
if (sqlStatement instanceof SQLSelectStatement) {
} catch (ParserException e) {
log.warn("解析sql失败:{}", originalSql, e);
}
}
// Mongodb is currently unable to recognize it, so every time a page is transmitted
if (!supportDruid || (sqlStatement instanceof SQLSelectStatement)) {
pageNo = Optional.ofNullable(param.getPageNo()).orElse(1);
pageSize = Optional.ofNullable(param.getPageSize()).orElse(EasyToolsConstant.MAX_PAGE_SIZE);
offset = (pageNo - 1) * pageSize;
count = pageSize;
sqlType = SqlTypeEnum.SELECT.getCode();
}
} catch (ParserException e) {
log.warn("解析sql失败:{}", originalSql, e);
}
ExecuteResult executeResult = null;
if (SqlTypeEnum.SELECT.getCode().equals(sqlType) && !SqlUtils.hasPageLimit(originalSql, dbType)) {
String pageLimit = Chat2DBContext.getSqlBuilder().pageLimit(originalSql, offset, pageNo, pageSize);
@ -161,14 +179,18 @@ public class DlTemplateServiceImpl implements DlTemplateService {
executeResult = execute(originalSql, offset, count);
}
executeResult.setSqlType(sqlType);
executeResult.setOriginalSql(originalSql);
boolean supportJsqlParser = !DataSourceTypeEnum.MONGODB.getCode().equals(type);
if (supportJsqlParser) {
try {
SqlUtils.buildCanEditResult(originalSql, dbType, executeResult);
} catch (Exception e) {
log.warn("buildCanEditResult error", e);
}
}
if (SqlTypeEnum.SELECT.getCode().equals(sqlType)) {
executeResult.setPageNo(pageNo);
executeResult.setPageSize(pageSize);
@ -182,7 +204,8 @@ public class DlTemplateServiceImpl implements DlTemplateService {
List<Header> headers = executeResult.getHeaderList();
if (executeResult.getSuccess() && executeResult.isCanEdit() && CollectionUtils.isNotEmpty(headers)) {
headers = setColumnInfo(headers, executeResult.getTableName(), param.getSchemaName(), param.getDatabaseName());
headers = setColumnInfo(headers, executeResult.getTableName(), param.getSchemaName(),
param.getDatabaseName());
}
Header rowNumberHeader = Header.builder()
.name(I18nUtils.getMessage("sqlResult.rowNumber"))
@ -285,7 +308,8 @@ public class DlTemplateServiceImpl implements DlTemplateService {
return keyColumns;
}
private String getDeleteSql(UpdateSelectResultParam param, List<String> row, MetaData metaSchema, List<String> keyColumns) {
private String getDeleteSql(UpdateSelectResultParam param, List<String> row, MetaData metaSchema,
List<String> keyColumns) {
StringBuilder script = new StringBuilder();
script.append("DELETE FROM ").append(param.getTableName()).append("");
@ -365,8 +389,8 @@ public class DlTemplateServiceImpl implements DlTemplateService {
}
private String getUpdateSql(UpdateSelectResultParam param, List<String> row, List<String> odlRow, MetaData metaSchema,
private String getUpdateSql(UpdateSelectResultParam param, List<String> row, List<String> odlRow,
MetaData metaSchema,
List<String> keyColumns, boolean copy) {
StringBuilder script = new StringBuilder();
if (CollectionUtils.isEmpty(row) || CollectionUtils.isEmpty(odlRow)) {
@ -402,7 +426,8 @@ public class DlTemplateServiceImpl implements DlTemplateService {
if (CollectionUtils.isEmpty(columns)) {
return headers;
}
Map<String, TableColumn> columnMap = columns.stream().collect(Collectors.toMap(TableColumn::getName, tableColumn -> tableColumn));
Map<String, TableColumn> columnMap = columns.stream().collect(
Collectors.toMap(TableColumn::getName, tableColumn -> tableColumn));
List<TableIndex> tableIndices = tableService.queryIndexes(tableQueryParam);
if (!CollectionUtils.isEmpty(tableIndices)) {
for (TableIndex tableIndex : tableIndices) {
@ -441,7 +466,8 @@ public class DlTemplateServiceImpl implements DlTemplateService {
ExecuteResult executeResult;
try {
ValueHandler valueHandler = Chat2DBContext.getMetaData().getValueHandler();
executeResult = SQLExecutor.getInstance().execute(sql, Chat2DBContext.getConnection(), true, offset, count,valueHandler);
executeResult = SQLExecutor.getInstance().execute(sql, Chat2DBContext.getConnection(), true, offset, count,
valueHandler);
} catch (SQLException e) {
log.warn("执行sql:{}异常", sql, e);
executeResult = ExecuteResult.builder()
@ -467,7 +493,8 @@ public class DlTemplateServiceImpl implements DlTemplateService {
createParam.setSchemaName(connectInfo.getSchemaName());
createParam.setUseTime(executeResult.getDuration());
createParam.setType(connectInfo.getDbType());
createParam.setOperationRows(executeResult.getUpdateCount() != null ? Long.valueOf(executeResult.getUpdateCount()) : null);
createParam.setOperationRows(
executeResult.getUpdateCount() != null ? Long.valueOf(executeResult.getUpdateCount()) : null);
operationLogService.create(createParam);
} catch (Exception e) {
log.error("addOperationLog error:", e);

View File

@ -20,6 +20,12 @@ public enum DataSourceTypeEnum implements BaseEnum<String> {
*/
REDIS("redis数据库连接"),
/**
* mongo数据库连接
*/
MONGODB("mongo数据库连接"),
;
final String description;

View File

@ -6,12 +6,14 @@ import java.util.List;
import ai.chat2db.server.domain.api.param.DlExecuteParam;
import ai.chat2db.server.domain.api.param.UpdateSelectResultParam;
import ai.chat2db.server.domain.api.service.DlTemplateService;
import ai.chat2db.server.tools.base.enums.DataSourceTypeEnum;
import ai.chat2db.server.tools.base.wrapper.result.DataResult;
import ai.chat2db.server.tools.base.wrapper.result.ListResult;
import ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;
import ai.chat2db.server.web.api.controller.rdb.converter.RdbWebConverter;
import ai.chat2db.server.web.api.controller.rdb.request.DdlCountRequest;
import ai.chat2db.server.web.api.controller.rdb.request.DmlRequest;
import ai.chat2db.server.web.api.controller.rdb.request.DmlTableRequest;
import ai.chat2db.server.web.api.controller.rdb.request.SelectResultUpdateRequest;
import ai.chat2db.server.web.api.controller.rdb.vo.ExecuteResultVO;
import ai.chat2db.spi.model.ExecuteResult;
@ -55,6 +57,25 @@ public class RdbDmlController {
return ListResult.of(resultVOS);
}
/**
* 查询表结构信息
*
* @param request
* @return
*/
@RequestMapping(value = "/execute_table", method = {RequestMethod.POST, RequestMethod.PUT})
public ListResult<ExecuteResultVO> executeTable(@RequestBody DmlTableRequest request) {
DlExecuteParam param = rdbWebConverter.request2param(request);
// 解析sql
String type = Chat2DBContext.getConnectInfo().getDbType();
if (DataSourceTypeEnum.MONGODB.getCode().equals(type)) {
param.setSql("db." + request.getTableName() + ".find()");
} else {
param.setSql("select * from " + request.getTableName());
}
return dlTemplateService.execute(param)
.map(rdbWebConverter::dto2vo);
}
/**
* update 查询结果
@ -72,13 +93,13 @@ public class RdbDmlController {
return DataResult.of(rdbWebConverter.dto2vo(result.getData()));
}
@RequestMapping(value = "/get_update_sql", method = {RequestMethod.POST, RequestMethod.PUT})
public DataResult<String> getUpdateSelectResultSql(@RequestBody SelectResultUpdateRequest request) {
UpdateSelectResultParam param = rdbWebConverter.request2param(request);
return dlTemplateService.updateSelectResult(param);
}
/**
* 增删改查等数据运维
*

View File

@ -7,7 +7,6 @@ import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.List;
import ai.chat2db.spi.jdbc.DefaultValueHandler;
import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.SQLUtils.FormatOption;
@ -28,11 +27,11 @@ import ai.chat2db.server.domain.api.enums.ExportSizeEnum;
import ai.chat2db.server.domain.api.enums.ExportTypeEnum;
import ai.chat2db.server.tools.base.excption.BusinessException;
import ai.chat2db.server.tools.common.exception.ParamBusinessException;
import ai.chat2db.server.tools.common.util.ConfigUtils;
import ai.chat2db.server.tools.common.util.EasyCollectionUtils;
import ai.chat2db.server.tools.common.util.EasyEnumUtils;
import ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;
import ai.chat2db.server.web.api.controller.rdb.request.DataExportRequest;
import ai.chat2db.spi.jdbc.DefaultValueHandler;
import ai.chat2db.spi.sql.Chat2DBContext;
import ai.chat2db.spi.sql.SQLExecutor;
import ai.chat2db.spi.util.JdbcUtils;
@ -92,11 +91,17 @@ public class RdbDmlExportController {
throw new ParamBusinessException("exportSize");
}
DbType dbType = JdbcUtils.parse2DruidDbType(Chat2DBContext.getConnectInfo().getDbType());
String tableName;
if (dbType != null) {
SQLStatement sqlStatement = SQLUtils.parseSingleStatement(sql, dbType);
if (!(sqlStatement instanceof SQLSelectStatement)) {
throw new BusinessException("dataSource.sqlAnalysisError");
}
String tableName = SqlUtils.getTableName(sql, dbType);
tableName = SqlUtils.getTableName(sql, dbType);
} else {
tableName = StringUtils.join(Lists.newArrayList(request.getDatabaseName(), request.getSchemaName()), "_");
}
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode(
tableName + "_" + LocalDateTime.now().format(DatePattern.PURE_DATETIME_FORMATTER),
@ -108,7 +113,6 @@ public class RdbDmlExportController {
} else {
doExportInsert(sql, response, fileName, dbType, tableName);
}
String SS = ConfigUtils.APP_PATH;
}
private void doExportCsv(String sql, HttpServletResponse response, String fileName)

View File

@ -41,6 +41,15 @@ public abstract class RdbWebConverter {
*/
public abstract DlExecuteParam request2param(DmlRequest request);
/**
* 参数转换
*
* @param request
* @return
*/
public abstract DlExecuteParam request2param(DmlTableRequest request);
/**
* 参数转换
*

View File

@ -0,0 +1,45 @@
package ai.chat2db.server.web.api.controller.rdb.request;
import ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest;
import ai.chat2db.server.web.api.controller.data.source.request.DataSourceConsoleRequestInfo;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* @author moji
* @version TableManageRequest.java, v 0.1 2022年09月16日 17:55 moji Exp $
* @date 2022/09/16
*/
@Data
public class DmlTableRequest extends DataSourceBaseRequest implements DataSourceConsoleRequestInfo {
/**
* 表的名词
*/
@NotNull
private String tableName;
/**
* 控制台id
*/
@NotNull
private Long consoleId;
/**
* 分页编码
* 只有select语句才有
*/
private Integer pageNo;
/**
* 分页大小
* 只有select语句才有
*/
private Integer pageSize;
/**
* 返回全部数据
* 只有select语句才有
*/
private Boolean pageSizeAll;
}

View File

@ -79,5 +79,11 @@
<artifactId>jts-core</artifactId>
<version>1.19.0</version> <!-- 确保使用最新的版本 -->
</dependency>
<!-- https://mvnrepository.com/artifact/org.mongodb/bson -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>bson</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,14 @@
package ai.chat2db.spi.sql;
import org.bson.json.Converter;
import org.bson.json.StrictJsonWriter;
import org.bson.types.ObjectId;
public class MongExtendedJsonObjectIdConverter implements Converter<ObjectId> {
@Override
public void convert(final ObjectId value, final StrictJsonWriter writer) {
writer.writeStartObject();
writer.writeString("", value.toHexString());
writer.writeEndObject();
}
}

View File

@ -1,25 +1,44 @@
package ai.chat2db.spi.sql;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import ai.chat2db.server.tools.base.constant.EasyToolsConstant;
import ai.chat2db.server.tools.base.enums.DataSourceTypeEnum;
import ai.chat2db.server.tools.common.util.I18nUtils;
import ai.chat2db.spi.ValueHandler;
import ai.chat2db.spi.jdbc.DefaultValueHandler;
import ai.chat2db.spi.model.*;
import ai.chat2db.spi.model.Database;
import ai.chat2db.spi.model.ExecuteResult;
import ai.chat2db.spi.model.Header;
import ai.chat2db.spi.model.Procedure;
import ai.chat2db.spi.model.Schema;
import ai.chat2db.spi.model.Table;
import ai.chat2db.spi.model.TableColumn;
import ai.chat2db.spi.model.TableIndex;
import ai.chat2db.spi.model.TableIndexColumn;
import ai.chat2db.spi.model.Type;
import ai.chat2db.spi.util.JdbcUtils;
import ai.chat2db.spi.util.ResultSetUtils;
import cn.hutool.core.date.TimeInterval;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.bson.Document;
import org.springframework.util.Assert;
import java.sql.*;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Dbhub 统一数据库连接管理
*
@ -143,7 +162,8 @@ public class SQLExecutor {
* @return
* @throws SQLException
*/
public ExecuteResult execute(final String sql, Connection connection, ValueHandler valueHandler) throws SQLException {
public ExecuteResult execute(final String sql, Connection connection, ValueHandler valueHandler)
throws SQLException {
return execute(sql, connection, true, null, null, valueHandler);
}
@ -157,7 +177,9 @@ public class SQLExecutor {
int affectedRows = stmt.executeUpdate(sql);
if (affectedRows != n) {
executeResult.setSuccess(false);
executeResult.setMessage("Update error " + sql + " update affectedRows = " + affectedRows + ", Each SQL statement should update no more than one record. Please use a unique key for updates.");
executeResult.setMessage("Update error " + sql + " update affectedRows = " + affectedRows
+ ", Each SQL statement should update no more than one record. Please use a unique key for "
+ "updates.");
// connection.rollback();
}
}
@ -180,10 +202,14 @@ public class SQLExecutor {
throws SQLException {
Assert.notNull(sql, "SQL must not be null");
log.info("execute:{}", sql);
String type = Chat2DBContext.getConnectInfo().getDbType();
ExecuteResult executeResult = ExecuteResult.builder().sql(sql).success(Boolean.TRUE).build();
try (Statement stmt = connection.createStatement()) {
stmt.setFetchSize(EasyToolsConstant.MAX_PAGE_SIZE);
//stmt.setQueryTimeout(30);
if (!DataSourceTypeEnum.MONGODB.getCode().equals(type)) {
stmt.setQueryTimeout(30);
}
if (offset != null && count != null) {
stmt.setMaxRows(offset + count);
}
@ -205,8 +231,14 @@ public class SQLExecutor {
executeResult.setHeaderList(headerList);
int chat2dbAutoRowIdIndex = -1;// chat2db自动生成的行分页ID
boolean isMongoMap = false;
for (int i = 1; i <= col; i++) {
String name = ResultSetUtils.getColumnName(resultSetMetaData, i);
// The returned map is from mongodb, and you need to parse the map yourself
if (DataSourceTypeEnum.MONGODB.getCode().equals(type) && i == 1 && "map".equals(name)) {
isMongoMap = true;
break;
}
if ("CAHT2DB_AUTO_ROW_ID".equals(name)) {
chat2dbAutoRowIdIndex = i;
continue;
@ -223,6 +255,13 @@ public class SQLExecutor {
List<List<String>> dataList = Lists.newArrayList();
executeResult.setDataList(dataList);
Map<String, Header> headerListMap = null;
List<Map<String, String>> dataListMap = null;
if (isMongoMap) {
headerListMap = Maps.newLinkedHashMap();
dataListMap = Lists.newArrayList();
}
if (offset == null || offset < 0) {
offset = 0;
}
@ -232,6 +271,7 @@ public class SQLExecutor {
if (rowNumber++ < offset) {
continue;
}
if (!isMongoMap) {
List<String> row = Lists.newArrayListWithExpectedSize(col);
dataList.add(row);
for (int i = 1; i <= col; i++) {
@ -240,10 +280,44 @@ public class SQLExecutor {
}
row.add(valueHandler.getString(rs, i, limitRowSize));
}
} else {
for (int i = 1; i <= col; i++) {
Object o = rs.getObject(i);
Map<String, String> row = Maps.newHashMap();
dataListMap.add(row);
if (o instanceof Document document) {
for (String string : document.keySet()) {
headerListMap.computeIfAbsent(string, k -> Header.builder()
.dataType("string")
.name(string)
.build());
row.put(string, Objects.toString(document.get(string)));
}
} else {
headerListMap.computeIfAbsent("_unknown", k -> Header.builder()
.dataType("string")
.name("_unknown")
.build());
row.put("_unknown", Objects.toString(o));
}
}
}
if (count != null && count > 0 && rowCount++ >= count) {
break;
}
}
if (isMongoMap) {
headerList.addAll(headerListMap.values().stream().toList());
for (Map<String, String> stringStringMap : dataListMap) {
List<String> dataTempList = Lists.newArrayList();
dataList.add(dataTempList);
for (Header value : headerListMap.values()) {
dataTempList.add(stringStringMap.get(value.getName()));
}
}
}
executeResult.setDuration(timeInterval.interval());
} finally {
JdbcUtils.closeResultSet(rs);
@ -257,7 +331,6 @@ public class SQLExecutor {
return executeResult;
}
/**
* 执行sql
*
@ -443,11 +516,14 @@ public class SQLExecutor {
}
}
/**
* Retrieves a description of all the data types supported by this database. They are ordered by DATA_TYPE and then by how closely the data type maps to the corresponding JDBC SQL type.
* If the database supports SQL distinct types, then getTypeInfo() will return a single row with a TYPE_NAME of DISTINCT and a DATA_TYPE of Types.DISTINCT. If the database supports SQL structured types, then getTypeInfo() will return a single row with a TYPE_NAME of STRUCT and a DATA_TYPE of Types.STRUCT.
* If SQL distinct or structured types are supported, then information on the individual types may be obtained from the getUDTs() method.
* Retrieves a description of all the data types supported by this database. They are ordered by DATA_TYPE and then
* by how closely the data type maps to the corresponding JDBC SQL type.
* If the database supports SQL distinct types, then getTypeInfo() will return a single row with a TYPE_NAME of
* DISTINCT and a DATA_TYPE of Types.DISTINCT. If the database supports SQL structured types, then getTypeInfo()
* will return a single row with a TYPE_NAME of STRUCT and a DATA_TYPE of Types.STRUCT.
* If SQL distinct or structured types are supported, then information on the individual types may be obtained from
* the getUDTs() method.
*
* @param connection connection
* @return List<Function>

View File

View File

@ -296,6 +296,13 @@
<version>4.4.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mongodb/bson -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>bson</artifactId>
<version>4.11.1</version>
</dependency>
</dependencies>
</dependencyManagement>