feat: stored procedures and funciton parameters support for mssql

This commit is contained in:
Nybkox
2024-11-26 17:05:12 +01:00
parent 35e9ff607d
commit 2b2ecac3ab
11 changed files with 162 additions and 14 deletions

View File

@@ -118,9 +118,23 @@ export interface ViewInfo extends SqlObjectInfo {
columns: ColumnInfo[]; columns: ColumnInfo[];
} }
export interface ProcedureInfo extends SqlObjectInfo {} export interface ParameterInfo {
objectId?: string | number;
parentObjectId?: string | number;
pureName: string;
dataType: string;
fullDataType: string;
maxLength?: number;
precision?: number;
scale?: string;
isOutputParameter?: boolean;
}
export interface ProcedureInfo extends SqlObjectInfo {
parameters?: ParameterInfo[];
}
export interface FunctionInfo extends SqlObjectInfo { export interface FunctionInfo extends SqlObjectInfo {
parameters?: ParameterInfo[];
// returnDataType?: string; // returnDataType?: string;
} }

View File

@@ -62,7 +62,7 @@
{#if (isExpanded || isExpandedBySearch) && subItemsComponent} {#if (isExpanded || isExpandedBySearch) && subItemsComponent}
<div class="subitems"> <div class="subitems">
<svelte:component <svelte:component
this={subItemsComponent} this={subItemsComponent(data)}
{data} {data}
{filter} {filter}
{passProps} {passProps}

View File

@@ -0,0 +1,18 @@
<script lang="ts" context="module">
export const extractKey = ({ columnName }) => columnName;
</script>
<script lang="ts">
import AppObjectCore from './AppObjectCore.svelte';
export let data;
</script>
<AppObjectCore
{...$$restProps}
{data}
title={data.pureName}
extInfo={data.isOutputParameter ? `data.fullDataType OUT` : data.fullDataType}
icon={'icon parameter'}
disableHover
/>

View File

@@ -0,0 +1,15 @@
<script lang="ts">
import AppObjectList from './AppObjectList.svelte';
import * as parameterAppObject from './ParameterAppObject.svelte';
export let data;
</script>
<AppObjectList
list={data.parameters ||
[].map(parameter => ({
...data,
...parameter,
}))}
module={parameterAppObject}
/>

View File

@@ -63,6 +63,7 @@
'icon open-in-new': 'mdi mdi-open-in-new', 'icon open-in-new': 'mdi mdi-open-in-new',
'icon add-folder': 'mdi mdi-folder-plus-outline', 'icon add-folder': 'mdi mdi-folder-plus-outline',
'icon add-column': 'mdi mdi-table-column-plus-after', 'icon add-column': 'mdi mdi-table-column-plus-after',
'icon parameter': 'mdi mdi-at',
'icon window-restore': 'mdi mdi-window-restore', 'icon window-restore': 'mdi mdi-window-restore',
'icon window-maximize': 'mdi mdi-window-maximize', 'icon window-maximize': 'mdi mdi-window-maximize',

View File

@@ -251,7 +251,7 @@
<AppObjectList <AppObjectList
list={connectionsWithParent} list={connectionsWithParent}
module={connectionAppObject} module={connectionAppObject}
subItemsComponent={SubDatabaseList} subItemsComponent={() => SubDatabaseList}
expandOnClick expandOnClick
isExpandable={data => $openedConnections.includes(data._id) && !data.singleDatabase} isExpandable={data => $openedConnections.includes(data._id) && !data.singleDatabase}
{filter} {filter}
@@ -275,7 +275,7 @@
<AppObjectList <AppObjectList
list={connectionsWithoutParent} list={connectionsWithoutParent}
module={connectionAppObject} module={connectionAppObject}
subItemsComponent={SubDatabaseList} subItemsComponent={() => SubDatabaseList}
expandOnClick expandOnClick
isExpandable={data => $openedConnections.includes(data._id) && !data.singleDatabase} isExpandable={data => $openedConnections.includes(data._id) && !data.singleDatabase}
{filter} {filter}

View File

@@ -52,6 +52,7 @@
import AppObjectListHandler from './AppObjectListHandler.svelte'; import AppObjectListHandler from './AppObjectListHandler.svelte';
import { matchDatabaseObjectAppObject } from '../appobj/appObjectTools'; import { matchDatabaseObjectAppObject } from '../appobj/appObjectTools';
import FocusedConnectionInfoWidget from './FocusedConnectionInfoWidget.svelte'; import FocusedConnectionInfoWidget from './FocusedConnectionInfoWidget.svelte';
import SubProcedureParamList from '../appobj/SubProcedureParamList.svelte';
export let conid; export let conid;
export let database; export let database;
@@ -232,9 +233,16 @@
.map(x => ({ ...x, conid, database }))} .map(x => ({ ...x, conid, database }))}
module={databaseObjectAppObject} module={databaseObjectAppObject}
groupFunc={data => getObjectTypeFieldLabel(data.objectTypeField, driver)} groupFunc={data => getObjectTypeFieldLabel(data.objectTypeField, driver)}
subItemsComponent={SubColumnParamList} subItemsComponent={data =>
data.objectTypeField == 'procedures' || data.objectTypeField == 'functions'
? SubProcedureParamList
: SubColumnParamList}
isExpandable={data => isExpandable={data =>
data.objectTypeField == 'tables' || data.objectTypeField == 'views' || data.objectTypeField == 'matviews'} data.objectTypeField == 'tables' ||
data.objectTypeField == 'views' ||
data.objectTypeField == 'matviews' ||
((data.objectTypeField == 'procedures' || data.objectTypeField == 'functions') &&
!!data.parameters?.length)}
expandIconFunc={chevronExpandIcon} expandIconFunc={chevronExpandIcon}
{filter} {filter}
passProps={{ passProps={{

View File

@@ -31,6 +31,18 @@ function simplifyComutedExpression(expr) {
return expr; return expr;
} }
function getFullDataTypeName({ dataType, charMaxLength, numericScale, numericPrecision }) {
let fullDataType = dataType;
if (charMaxLength && isTypeString(dataType)) {
fullDataType = `${dataType}(${charMaxLength < 0 ? 'MAX' : charMaxLength})`;
}
if (numericPrecision && numericScale && isTypeNumeric(dataType)) {
fullDataType = `${dataType}(${numericPrecision},${numericScale})`;
}
return fullDataType;
}
function getColumnInfo({ function getColumnInfo({
isNullable, isNullable,
isIdentity, isIdentity,
@@ -43,13 +55,12 @@ function getColumnInfo({
defaultConstraint, defaultConstraint,
computedExpression, computedExpression,
}) { }) {
let fullDataType = dataType; const fullDataType = getFullDataTypeName({
if (charMaxLength && isTypeString(dataType)) { dataType,
fullDataType = `${dataType}(${charMaxLength < 0 ? 'MAX' : charMaxLength})`; charMaxLength,
} numericPrecision,
if (numericPrecision && numericScale && isTypeNumeric(dataType)) { numericScale,
fullDataType = `${dataType}(${numericPrecision},${numericScale})`; });
}
if (defaultValue) { if (defaultValue) {
defaultValue = defaultValue.trim(); defaultValue = defaultValue.trim();
@@ -116,7 +127,11 @@ class MsSqlAnalyser extends DatabaseAnalyser {
this.feedback({ analysingMessage: 'Loading views' }); this.feedback({ analysingMessage: 'Loading views' });
const viewsRows = await this.analyserQuery('views', ['views']); const viewsRows = await this.analyserQuery('views', ['views']);
this.feedback({ analysingMessage: 'Loading procedures & functions' }); this.feedback({ analysingMessage: 'Loading procedures & functions' });
const programmableRows = await this.analyserQuery('programmables', ['procedures', 'functions']); const programmableRows = await this.analyserQuery('programmables', ['procedures', 'functions']);
const prodceureParameterRows = await this.analyserQuery('proceduresParameters');
const functionParameterRows = await this.analyserQuery('functionParameters');
this.feedback({ analysingMessage: 'Loading view columns' }); this.feedback({ analysingMessage: 'Loading view columns' });
const viewColumnRows = await this.analyserQuery('viewColumns', ['views']); const viewColumnRows = await this.analyserQuery('viewColumns', ['views']);
@@ -157,20 +172,46 @@ class MsSqlAnalyser extends DatabaseAnalyser {
columns: viewColumnRows.rows.filter(col => col.objectId == row.objectId).map(getColumnInfo), columns: viewColumnRows.rows.filter(col => col.objectId == row.objectId).map(getColumnInfo),
})); }));
const prodceureParameter = prodceureParameterRows.rows.map(row => ({
...row,
fullDataType: getFullDataTypeName(row),
}));
const prodceureToParameters = prodceureParameter.reduce((acc, parameter) => {
if (!acc[parameter.parentObjectId]) acc[parameter.parentObjectId] = [];
acc[parameter.parentObjectId].push(parameter);
return acc;
}, {});
const procedures = programmableRows.rows const procedures = programmableRows.rows
.filter(x => x.sqlObjectType.trim() == 'P') .filter(x => x.sqlObjectType.trim() == 'P')
.map(row => ({ .map(row => ({
...row, ...row,
contentHash: row.modifyDate && row.modifyDate.toISOString(), contentHash: row.modifyDate && row.modifyDate.toISOString(),
createSql: getCreateSql(row), createSql: getCreateSql(row),
parameters: prodceureToParameters[row.objectId],
})); }));
const functionParameters = functionParameterRows.rows.map(row => ({
...row,
fullDataType: getFullDataTypeName(row),
}));
const functionToParameters = functionParameters.reduce((acc, parameter) => {
if (!acc[parameter.parentObjectId]) acc[parameter.parentObjectId] = [];
acc[parameter.parentObjectId].push(parameter);
return acc;
}, {});
const functions = programmableRows.rows const functions = programmableRows.rows
.filter(x => ['FN', 'IF', 'TF'].includes(x.sqlObjectType.trim())) .filter(x => ['FN', 'IF', 'TF'].includes(x.sqlObjectType.trim()))
.map(row => ({ .map(row => ({
...row, ...row,
contentHash: row.modifyDate && row.modifyDate.toISOString(), contentHash: row.modifyDate && row.modifyDate.toISOString(),
createSql: getCreateSql(row), createSql: getCreateSql(row),
parameters: functionToParameters[row.objectId],
})); }));
this.feedback({ analysingMessage: null }); this.feedback({ analysingMessage: null });

View File

@@ -0,0 +1,26 @@
module.exports = `
SELECT
o.object_id as parentObjectId,
p.object_id AS parameterObjectId,
CASE
WHEN p.name IS NULL OR LTRIM(RTRIM(p.name)) = '' THEN
'@Output'
ELSE
p.name
END AS pureName,
TYPE_NAME(p.user_type_id) AS dataType,
p.max_length AS charMaxLength,
p.precision AS precision,
p.scale AS scale,
p.is_output AS isOutputParameter,
p.parameter_id AS parameterIndex
FROM
sys.objects o
JOIN
sys.parameters p ON o.object_id = p.object_id
WHERE
o.type IN ('FN', 'IF', 'TF')
ORDER BY
p.object_id,
p.parameter_id;
`;

View File

@@ -7,6 +7,8 @@ const modifications = require('./modifications');
const loadSqlCode = require('./loadSqlCode'); const loadSqlCode = require('./loadSqlCode');
const views = require('./views'); const views = require('./views');
const programmables = require('./programmables'); const programmables = require('./programmables');
const proceduresParameters = require('./proceduresParameters');
const functionParameters = require('./functionParameters');
const viewColumns = require('./viewColumns'); const viewColumns = require('./viewColumns');
const indexes = require('./indexes'); const indexes = require('./indexes');
const indexcols = require('./indexcols'); const indexcols = require('./indexcols');
@@ -20,6 +22,8 @@ module.exports = {
loadSqlCode, loadSqlCode,
views, views,
programmables, programmables,
proceduresParameters,
functionParameters,
viewColumns, viewColumns,
indexes, indexes,
indexcols, indexcols,

View File

@@ -0,0 +1,21 @@
module.exports = `
SELECT
o.object_id as parentObjectId,
p.object_id as objectId,
p.name AS pureName,
TYPE_NAME(p.user_type_id) AS dataType,
p.max_length AS charMaxLength,
p.precision AS precision,
p.scale AS scale,
p.is_output AS isOutputParameter,
p.parameter_id AS parameterIndex
FROM
sys.objects o
JOIN
sys.parameters p ON o.object_id = p.object_id
WHERE
o.type = 'P'
ORDER BY
o.object_id,
p.parameter_id;
`;