diff --git a/packages/tools/src/driverBase.ts b/packages/tools/src/driverBase.ts index 531b37d7b..9705b381b 100644 --- a/packages/tools/src/driverBase.ts +++ b/packages/tools/src/driverBase.ts @@ -1,6 +1,20 @@ +import { SqlDumper } from "./SqlDumper"; + +const dialect = { + limitSelect: true, + rangeSelect: true, + offsetFetchRangeSyntax: true, + stringEscapeChar: "'", + fallbackDataType: 'nvarchar(max)', + quoteIdentifier(s) { + return s; + }, +}; + export const driverBase = { analyserClass: null, - dumperClass: null, + dumperClass: SqlDumper, + dialect, async analyseFull(pool) { const analyser = new this.analyserClass(pool, this); diff --git a/packages/tools/src/packageTools.ts b/packages/tools/src/packageTools.ts index 0aa87b4c1..6fd1d5639 100644 --- a/packages/tools/src/packageTools.ts +++ b/packages/tools/src/packageTools.ts @@ -1,4 +1,7 @@ +import { EngineDriver, ExtensionsDirectory } from 'dbgate-types'; import _camelCase from 'lodash/camelCase'; +import _isString from 'lodash/isString'; +import _isPlainObject from 'lodash/isPlainObject'; export function extractShellApiPlugins(functionName, props): string[] { const res = []; @@ -22,3 +25,16 @@ export function extractShellApiFunctionName(functionName) { } return `dbgateApi.${functionName}`; } + +export function findEngineDriver(connection, extensions: ExtensionsDirectory): EngineDriver { + if (_isString(connection)) { + return extensions.drivers.find((x) => x.engine == connection); + } + if (_isPlainObject(connection)) { + const { engine } = connection; + if (engine) { + return extensions.drivers.find((x) => x.engine == engine); + } + } + return null; +} diff --git a/packages/types/engines.d.ts b/packages/types/engines.d.ts index 9dc88dec3..07bdfd4b4 100644 --- a/packages/types/engines.d.ts +++ b/packages/types/engines.d.ts @@ -20,6 +20,7 @@ export interface WriteTableOptions { export interface EngineDriver { engine: string; + title: string; connect(nativeModules, { server, port, user, password, database }): any; query(pool: any, sql: string): Promise; stream(pool: any, sql: string, options: StreamOptions); diff --git a/packages/types/extensions.d.ts b/packages/types/extensions.d.ts index 5bd430ddb..94476a4c4 100644 --- a/packages/types/extensions.d.ts +++ b/packages/types/extensions.d.ts @@ -1,3 +1,5 @@ +import { EngineDriver } from "./engines"; + export interface FileFormatDefinition { storageType: string; extension: string; @@ -27,4 +29,5 @@ export interface PluginDefinition { export interface ExtensionsDirectory { plugins: PluginDefinition[]; fileFormats: FileFormatDefinition[]; + drivers: EngineDriver[]; } diff --git a/packages/web/src/datagrid/TableDataGrid.js b/packages/web/src/datagrid/TableDataGrid.js index 039ee89f2..4239e7da1 100644 --- a/packages/web/src/datagrid/TableDataGrid.js +++ b/packages/web/src/datagrid/TableDataGrid.js @@ -4,13 +4,14 @@ import DataGrid from './DataGrid'; import styled from 'styled-components'; import { TableGridDisplay, createGridConfig, createGridCache } from 'dbgate-datalib'; import { getFilterValueExpression } from 'dbgate-filterparser'; +import { findEngineDriver } from 'dbgate-tools'; import { useConnectionInfo, getTableInfo, useDatabaseInfo } from '../utility/metadataLoaders'; -import engines from 'dbgate-engines'; import useSocket from '../utility/SocketProvider'; import { VerticalSplitter } from '../widgets/Splitter'; import stableStringify from 'json-stable-stringify'; import ReferenceHeader from './ReferenceHeader'; import SqlDataGridCore from './SqlDataGridCore'; +import useExtensions from '../utility/useExtensions'; const ReferenceContainer = styled.div` position: absolute; @@ -49,6 +50,7 @@ export default function TableDataGrid({ const [childCache, setChildCache] = React.useState(createGridCache()); const [refReloadToken, setRefReloadToken] = React.useState(0); const [myLoadedTime, setMyLoadedTime] = React.useState(0); + const extension = useExtensions(); const { childConfig } = config; const setChildConfig = (value, reference = undefined) => { @@ -75,7 +77,7 @@ export default function TableDataGrid({ return connection ? new TableGridDisplay( { schemaName, pureName }, - engines(connection), + findEngineDriver(connection, extension), config, setConfig, cache || myCache, diff --git a/packages/web/src/impexp/createImpExpScript.js b/packages/web/src/impexp/createImpExpScript.js index e9752c221..1df26395e 100644 --- a/packages/web/src/impexp/createImpExpScript.js +++ b/packages/web/src/impexp/createImpExpScript.js @@ -2,8 +2,7 @@ import _ from 'lodash'; import ScriptWriter from './ScriptWriter'; import getAsArray from '../utility/getAsArray'; import { getConnectionInfo } from '../utility/metadataLoaders'; -import engines from 'dbgate-engines'; -import { findObjectLike } from 'dbgate-tools'; +import { findEngineDriver, findObjectLike } from 'dbgate-tools'; import { findFileFormat } from '../utility/fileformats'; export function getTargetName(extensions, source, values) { @@ -26,10 +25,10 @@ function extractApiParameters(values, direction, format) { return _.fromPairs(pairs); } -async function getConnection(storageType, conid, database) { +async function getConnection(extensions, storageType, conid, database) { if (storageType == 'database' || storageType == 'query') { const conn = await getConnectionInfo({ conid }); - const driver = engines(conn); + const driver = findEngineDriver(conn, extensions); return [ { ..._.pick(conn, ['server', 'engine', 'user', 'password', 'port']), @@ -154,11 +153,13 @@ export default async function createImpExpScript(extensions, values, addEditorIn const script = new ScriptWriter(); const [sourceConnection, sourceDriver] = await getConnection( + extensions, values.sourceStorageType, values.sourceConnectionId, values.sourceDatabaseName ); const [targetConnection, targetDriver] = await getConnection( + extensions, values.targetStorageType, values.targetConnectionId, values.targetDatabaseName @@ -223,6 +224,7 @@ export function getActionOptions(extensions, source, values, targetDbinfo) { export async function createPreviewReader(extensions, values, sourceName) { const [sourceConnection, sourceDriver] = await getConnection( + extensions, values.sourceStorageType, values.sourceConnectionId, values.sourceDatabaseName diff --git a/packages/web/src/modals/ConnectionModal.js b/packages/web/src/modals/ConnectionModal.js index 4a2558258..f965637d1 100644 --- a/packages/web/src/modals/ConnectionModal.js +++ b/packages/web/src/modals/ConnectionModal.js @@ -7,10 +7,12 @@ import { Formik, Form } from 'formik'; import ModalHeader from './ModalHeader'; import ModalFooter from './ModalFooter'; import ModalContent from './ModalContent'; +import useExtensions from '../utility/useExtensions'; // import FormikForm from '../utility/FormikForm'; export default function ConnectionModal({ modalState, connection = undefined }) { const [sqlConnectResult, setSqlConnectResult] = React.useState('Not connected'); + const extensions = useExtensions(); const handleTest = async (values) => { const resp = await axios.post('connections/test', values); @@ -31,9 +33,15 @@ export default function ConnectionModal({ modalState, connection = undefined })
- + + {extensions.drivers.map((driver) => ( + + ))} + {/* - + */} diff --git a/packages/web/src/tabs/QueryTab.js b/packages/web/src/tabs/QueryTab.js index 2f507071d..c51eca95b 100644 --- a/packages/web/src/tabs/QueryTab.js +++ b/packages/web/src/tabs/QueryTab.js @@ -2,14 +2,8 @@ import React from 'react'; import ReactDOM from 'react-dom'; import _ from 'lodash'; import axios from '../utility/axios'; -import engines from 'dbgate-engines'; -import { - useConnectionInfo, - getDbCore, - getConnectionInfo, - getSqlObjectInfo, -} from '../utility/metadataLoaders'; +import { useConnectionInfo, getDbCore, getConnectionInfo, getSqlObjectInfo } from '../utility/metadataLoaders'; import SqlEditor from '../sqleditor/SqlEditor'; import { useUpdateDatabaseForTab, useSetOpenedTabs, useOpenedTabs } from '../utility/globalState'; import QueryToolbar from '../query/QueryToolbar'; @@ -23,15 +17,18 @@ import useSocket from '../utility/SocketProvider'; import SaveSqlFileModal from '../modals/SaveSqlFileModal'; import useModalState from '../modals/useModalState'; import sqlFormatter from 'sql-formatter'; +import useExtensions from '../utility/useExtensions'; +import { driverBase, findEngineDriver } from 'dbgate-tools'; function useSqlTemplate(sqlTemplate, props) { const [sql, setSql] = React.useState(''); + const extensions = useExtensions(); async function loadTemplate() { if (sqlTemplate == 'CREATE TABLE') { const tableInfo = await getDbCore(props, props.objectTypeField || 'tables'); const connection = await getConnectionInfo(props); - const driver = engines(connection.engine); + const driver = findEngineDriver(connection, extensions) || driverBase; const dmp = driver.createDumper(); if (tableInfo) dmp.createTable(tableInfo); setSql(dmp.s); @@ -47,7 +44,7 @@ function useSqlTemplate(sqlTemplate, props) { const procedureInfo = await getSqlObjectInfo(props); const connection = await getConnectionInfo(props); - const driver = engines(connection.engine); + const driver = findEngineDriver(connection, extensions) || driverBase; const dmp = driver.createDumper(); if (procedureInfo) dmp.put('^execute %f', procedureInfo); setSql(dmp.s); diff --git a/packages/web/src/tabs/ViewDataTab.js b/packages/web/src/tabs/ViewDataTab.js index 65f751c4e..c3787fed3 100644 --- a/packages/web/src/tabs/ViewDataTab.js +++ b/packages/web/src/tabs/ViewDataTab.js @@ -2,18 +2,20 @@ import React from 'react'; import DataGrid from '../datagrid/DataGrid'; import { ViewGridDisplay, createGridCache, createChangeSet } from 'dbgate-datalib'; import { useConnectionInfo, useViewInfo } from '../utility/metadataLoaders'; -import engines from 'dbgate-engines'; import useUndoReducer from '../utility/useUndoReducer'; import usePropsCompare from '../utility/usePropsCompare'; import { useUpdateDatabaseForTab } from '../utility/globalState'; import useGridConfig from '../utility/useGridConfig'; import SqlDataGridCore from '../datagrid/SqlDataGridCore'; +import useExtensions from '../utility/useExtensions'; +import { findEngineDriver } from 'dbgate-tools'; export default function ViewDataTab({ conid, database, schemaName, pureName, tabVisible, toolbarPortalRef, tabid }) { const viewInfo = useViewInfo({ conid, database, schemaName, pureName }); const [config, setConfig] = useGridConfig(tabid); const [cache, setCache] = React.useState(createGridCache()); const [changeSetState, dispatchChangeSet] = useUndoReducer(createChangeSet()); + const extensions = useExtensions() useUpdateDatabaseForTab(tabVisible, conid, database); const connection = useConnectionInfo({ conid }); @@ -25,7 +27,7 @@ export default function ViewDataTab({ conid, database, schemaName, pureName, tab viewInfo && connection ? new ViewGridDisplay( viewInfo, - engines(connection), + findEngineDriver(connection, extensions), //@ts-ignore config, setConfig, diff --git a/packages/web/src/utility/useExtensions.js b/packages/web/src/utility/useExtensions.js index 71a92b6ce..b428f8241 100644 --- a/packages/web/src/utility/useExtensions.js +++ b/packages/web/src/utility/useExtensions.js @@ -10,11 +10,21 @@ export function ExtensionsProvider({ children }) { return {children}; } +function buildDrivers(plugins) { + const res = []; + for (const { content } of plugins) { + if (content.driver) res.push(content.driver); + if (content.drivers) res.push(...content.drivers); + } + return res; +} + export function buildExtensions(plugins) { /** @type {import('dbgate-types').ExtensionsDirectory} */ const extensions = { plugins, fileFormats: buildFileFormats(plugins), + drivers: buildDrivers(plugins), }; return extensions; }