mirror of
https://github.com/CodePhiliaX/Chat2DB.git
synced 2025-08-06 18:15:56 +08:00
merge mongo
This commit is contained in:
@ -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',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
@ -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>();
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -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 {
|
||||
|
@ -102,7 +102,7 @@ function CascaderDB(props: IProps) {
|
||||
if (curDataSourceId === undefined) {
|
||||
return;
|
||||
}
|
||||
const databaseList = await connection.getDBList({
|
||||
const databaseList = await connection.getDatabaseList({
|
||||
dataSourceId: curDataSourceId,
|
||||
});
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
}
|
||||
.downloadTextError{
|
||||
color: var(--color-error-text);
|
||||
cursor: pointer;
|
||||
}
|
||||
.downloadTextLoading{
|
||||
display: flex;
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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="𐂾" />
|
||||
</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="" />
|
||||
<div className={styles.boundInfoName}>{boundInfo.databaseName || `<${'database'}>`}</div>
|
||||
<Iconfont code="𐂾" />
|
||||
</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="" />
|
||||
<div className={styles.boundInfoName}>{boundInfo.schemaName || `<${'schema'}>`}</div>
|
||||
<Iconfont code="𐂾" />
|
||||
</div>
|
||||
</Dropdown>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
)
|
||||
|
||||
export default SelectBoundInfo;
|
||||
|
@ -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}
|
||||
|
@ -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) => {
|
||||
|
36
chat2db-client/src/hooks/getConnection.ts
Normal file
36
chat2db-client/src/hooks/getConnection.ts
Normal 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;
|
@ -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,
|
||||
};
|
||||
|
@ -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;
|
@ -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;
|
||||
|
32
chat2db-client/src/pages/main/store/main/index.ts
Normal file
32
chat2db-client/src/pages/main/store/main/index.ts
Normal 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,
|
||||
});
|
||||
};
|
@ -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,
|
||||
},
|
||||
})
|
||||
|
@ -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(() => {
|
||||
|
@ -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,
|
||||
|
@ -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) => {
|
||||
|
@ -31,8 +31,8 @@ export const useWorkspaceStore: UseBoundStoreWithEqualityFn<StoreApi<IStore>> =
|
||||
},
|
||||
),
|
||||
{
|
||||
name: "workspaceStore"
|
||||
}
|
||||
name: 'workspaceStore',
|
||||
},
|
||||
),
|
||||
shallow
|
||||
shallow,
|
||||
);
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -20,6 +20,12 @@ public enum DataSourceTypeEnum implements BaseEnum<String> {
|
||||
*/
|
||||
REDIS("redis数据库连接"),
|
||||
|
||||
|
||||
/**
|
||||
* mongo数据库连接
|
||||
*/
|
||||
MONGODB("mongo数据库连接"),
|
||||
|
||||
;
|
||||
|
||||
final String description;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 增删改查等数据运维
|
||||
*
|
||||
|
@ -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)
|
||||
|
@ -41,6 +41,15 @@ public abstract class RdbWebConverter {
|
||||
*/
|
||||
public abstract DlExecuteParam request2param(DmlRequest request);
|
||||
|
||||
|
||||
/**
|
||||
* 参数转换
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public abstract DlExecuteParam request2param(DmlTableRequest request);
|
||||
|
||||
/**
|
||||
* 参数转换
|
||||
*
|
||||
|
@ -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;
|
||||
}
|
@ -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>
|
@ -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();
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
0
chat2db-server/identifier.sqlite
Normal file
0
chat2db-server/identifier.sqlite
Normal 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>
|
||||
|
||||
|
Reference in New Issue
Block a user