diff --git a/packages/tools/src/SqlDumper.ts b/packages/tools/src/SqlDumper.ts index b1ad67fbf..589f877f5 100644 --- a/packages/tools/src/SqlDumper.ts +++ b/packages/tools/src/SqlDumper.ts @@ -17,6 +17,7 @@ import type { CheckInfo, AlterProcessor, SqlObjectInfo, + CallableObjectInfo, } from 'dbgate-types'; import _isString from 'lodash/isString'; import _isNumber from 'lodash/isNumber'; @@ -782,4 +783,9 @@ export class SqlDumper implements AlterProcessor { this.endCommand(); } } + + declareVariable(name: string, type: string, defaultValueLiteral?: string) {} + executeCallable(func: CallableObjectInfo, argLiterals: string[]) { + this.putCmd('^call %f(%,s)', func, argLiterals); + } } diff --git a/packages/types/dbinfo.d.ts b/packages/types/dbinfo.d.ts index 24d379c61..609669fa2 100644 --- a/packages/types/dbinfo.d.ts +++ b/packages/types/dbinfo.d.ts @@ -120,21 +120,19 @@ export interface ViewInfo extends SqlObjectInfo { export type ParameterMode = 'IN' | 'OUT' | 'INOUT' | 'RETURN'; -export interface ParameterInfo { - schemaName: string; +export interface ParameterInfo extends NamedObjectInfo { parameterName?: string; - pureName: string; dataType: string; parameterMode?: ParameterMode; } -export interface ProcedureInfo extends SqlObjectInfo { + +export interface CallableObjectInfo extends SqlObjectInfo { parameters?: ParameterInfo[]; } -export interface FunctionInfo extends SqlObjectInfo { - parameters?: ParameterInfo[]; - // returnDataType?: string; -} +export interface ProcedureInfo extends CallableObjectInfo {} + +export interface FunctionInfo extends CallableObjectInfo {} export interface TriggerInfo extends SqlObjectInfo {} diff --git a/packages/types/dumper.d.ts b/packages/types/dumper.d.ts index 8c3f0147b..12c1ce669 100644 --- a/packages/types/dumper.d.ts +++ b/packages/types/dumper.d.ts @@ -1,5 +1,5 @@ import { AlterProcessor } from './alter-processor'; -import { TableInfo } from './dbinfo'; +import { CallableObjectInfo, NamedObjectInfo, TableInfo } from './dbinfo'; import { SqlDialect } from './dialect'; export type TransformType = 'GROUP:YEAR' | 'GROUP:MONTH' | 'GROUP:DAY' | 'YEAR' | 'MONTH' | 'DAY'; // | 'GROUP:HOUR' | 'GROUP:MINUTE'; @@ -17,6 +17,9 @@ export interface SqlDumper extends AlterProcessor { createDatabase(name: string); dropDatabase(name: string); + declareVariable(name: string, type: string, defaultValueLiteral?: string); + executeCallable(func: CallableObjectInfo, argLiterals: string[]); + endCommand(); allowIdentityInsert(table: NamedObjectInfo, allow: boolean); truncateTable(table: NamedObjectInfo); diff --git a/packages/web/src/query/useEditorData.ts b/packages/web/src/query/useEditorData.ts index 5fa7a25b5..3a1bd52f4 100644 --- a/packages/web/src/query/useEditorData.ts +++ b/packages/web/src/query/useEditorData.ts @@ -55,12 +55,13 @@ export default function useEditorData({ tabid, reloadToken = 0, loadFromArgs = n // mark as not saved changeCounter += 1; } catch (err) { - const message = (err && err.response && err.response.data && err.response.data.error) || 'Loading failed'; + const message = + (err && err.response && err.response.data && err.response.data.error) || err.message || 'Loading failed'; editorState.update(x => ({ ...x, errorMessage: message, })); - console.error(err.response); + console.error(err.response || err.message); } } else { const initFallback = getParsedLocalStorage(localStorageKey); diff --git a/packages/web/src/utility/applyScriptTemplate.ts b/packages/web/src/utility/applyScriptTemplate.ts index 784de2aef..f4ed28b0b 100644 --- a/packages/web/src/utility/applyScriptTemplate.ts +++ b/packages/web/src/utility/applyScriptTemplate.ts @@ -70,13 +70,29 @@ export default async function applyScriptTemplate( return createSql.replace(/^\s*create\s+/i, alterPrefix); } } - if (scriptTemplate == 'EXECUTE PROCEDURE') { + if (scriptTemplate == 'EXECUTE PROCEDURE' || scriptTemplate == 'CALL FUNCTION') { const procedureInfo = dbinfo ? extractDbObjectInfo(dbinfo, props) : await getSqlObjectInfo(props); const connection = connectionInfo || (await getConnectionInfo(props)); const driver = findEngineDriver(connection, extensions) || driverBase; const dmp = driver.createDumper(); - if (procedureInfo) dmp.put('^execute %f', procedureInfo); + if (procedureInfo) { + const sqlVars = []; + for (const param of procedureInfo.parameters || []) { + const sqlVarName = param.parameterName?.startsWith('@') + ? param.parameterName?.substring(1) + : param.parameterName; + + dmp.declareVariable( + param.parameterName, + param.dataType, + param.parameterMode == 'OUT' ? null : `:${sqlVarName}` + ); + sqlVars.push(param.parameterName); + } + dmp.executeCallable(procedureInfo, sqlVars); + } + // if (procedureInfo) dmp.put('^execute %f', procedureInfo); return dmp.s; } @@ -159,6 +175,10 @@ export function getSupportedScriptTemplates(objectTypeField: string): { label: s label: ' ALTER FUNCTION', scriptTemplate: 'ALTER OBJECT', }, + { + label: 'CALL', + scriptTemplate: 'CALL FUNCTION', + }, ]; } diff --git a/plugins/dbgate-plugin-mssql/src/frontend/MsSqlDumper.js b/plugins/dbgate-plugin-mssql/src/frontend/MsSqlDumper.js index b87c6b44e..f15994468 100644 --- a/plugins/dbgate-plugin-mssql/src/frontend/MsSqlDumper.js +++ b/plugins/dbgate-plugin-mssql/src/frontend/MsSqlDumper.js @@ -1,4 +1,5 @@ const { SqlDumper, testEqualColumns, arrayToHexString } = global.DBGATE_PACKAGES['dbgate-tools']; +const _zip = require('lodash/zip'); class MsSqlDumper extends SqlDumper { constructor(driver, options) { @@ -160,6 +161,28 @@ class MsSqlDumper extends SqlDumper { selectScopeIdentity() { this.put('^select ^scope_identity()'); } + + declareVariable(name, type, defaultValueLiteral) { + this.put('^declare %s %s', name, type); + if (defaultValueLiteral) { + this.put(' = %s', defaultValueLiteral); + } + this.endCommand(); + } + + executeCallable(func, argLiterals) { + console.log('executeCallable', func, argLiterals); + if (func.objectTypeField == 'procedures') { + this.put('^execute %f&>&n', func, argLiterals); + + this.putCollection(',&n', _zip(func.parameters || [], argLiterals || []), ([param, value]) => { + this.putRaw(value); + if (param?.parameterMode == 'OUT') this.put(' ^output'); + }); + this.put('&<&n'); + this.endCommand(); + } + } } MsSqlDumper.prototype.renameView = MsSqlDumper.prototype.renameObject;