Skip to content

Commit 2929931

Browse files
authored
Merge pull request #214 from codefori/feature/self_stack
Add SelfIleStackFrame and InitialStackData interfaces for SELF
2 parents 10e6b1b + d5112a0 commit 2929931

File tree

6 files changed

+159
-45
lines changed

6 files changed

+159
-45
lines changed

src/config.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export async function onConnectOrServerInstall(): Promise<boolean> {
3131
const instance = getInstance();
3232

3333
Config.setConnectionName(instance.getConnection().currentConnectionName);
34+
determineFeatures();
3435

3536
await ServerComponent.initialise().then(installed => {
3637
if (installed) {
@@ -75,8 +76,6 @@ export async function onConnectOrServerInstall(): Promise<boolean> {
7576
export function setupConfig(context: ExtensionContext) {
7677
Config = new ConnectionStorage(context);
7778

78-
getInstance().onEvent(`connected`, onConnectOrServerInstall);
79-
8079
getInstance().onEvent(`disconnected`, async () => {
8180
JobManagerView.setVisible(false);
8281
JobManager.endAll();

src/extension.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as JSONServices from "./language/json";
77
import * as resultsProvider from "./views/results";
88

99
import { getInstance, loadBase } from "./base";
10-
import { JobManager, determineFeatures, fetchSystemInfo, setupConfig, turnOffAllFeatures } from "./config";
10+
import { JobManager, fetchSystemInfo, onConnectOrServerInstall, setupConfig, turnOffAllFeatures } from "./config";
1111
import { queryHistory } from "./views/queryHistoryView";
1212
import { ExampleBrowser } from "./views/examples/exampleBrowser";
1313
import { languageInit } from "./language";
@@ -83,8 +83,8 @@ export function activate(context: vscode.ExtensionContext): Db2i {
8383
instance.onEvent(`connected`, () => {
8484
// We need to fetch the system info
8585
fetchSystemInfo().then(() => {
86-
determineFeatures();
8786
// Refresh the examples when we have it, so we only display certain examples
87+
onConnectOrServerInstall();
8888
exampleBrowser.refresh();
8989
selfCodesView.setRefreshEnabled(Configuration.get(`autoRefreshSelfCodesView`) || false)
9090
})

src/views/examples/exampleBrowser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { getInstance } from "../../base";
44
import { OSData, fetchSystemInfo } from "../../config";
55
import { getServiceInfo } from "../../database/serviceInfo";
66

7-
const openExampleCommand = `vscode-db2i.examples.open`;
7+
export const openExampleCommand = `vscode-db2i.examples.open`;
88

99
export class ExampleBrowser implements TreeDataProvider<any> {
1010
private _onDidChangeTreeData: EventEmitter<TreeItem | undefined | null | void> = new EventEmitter<TreeItem | undefined | null | void>();

src/views/jobManager/jobManagerView.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ export class JobManagerView implements TreeDataProvider<any> {
175175
quickPick.onDidChangeSelection(async () => {
176176
const selections = quickPick.selectedItems;
177177
// SET SYSIBMADM.SELFCODES = SYSIBMADM.VALIDATE_SELF('-514, -204, -501, +30, -199');
178-
if (selections && selections[0].label !== currentSelfCodes) {
178+
if (selections && selections[0] && selections[0].label !== currentSelfCodes) {
179179
const code = selections[0].label as SelfValue;
180180
try {
181181
await selected.job.setSelfState(code);

src/views/jobManager/selfCodes/nodes.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
2+
export interface SelfIleStackFrame {
3+
ORD: number;
4+
TYPE: string;
5+
LIB: string;
6+
PGM: string;
7+
MODULE: string;
8+
PROC: string;
9+
STMT: string;
10+
ACTGRP: string;
11+
}
12+
13+
export interface InitialStackData {
14+
initial_stack: SelfIleStackFrame[];
15+
}
16+
117
export interface SelfCodeNode {
218
JOB_NAME: string,
319
USER_NAME: string;
@@ -9,6 +25,14 @@ export interface SelfCodeNode {
925
STMTTEXT: string;
1026
MESSAGE_TEXT: string;
1127
MESSAGE_SECOND_LEVEL_TEXT: string;
28+
29+
PROGRAM_LIBRARY: string;
30+
PROGRAM_NAME: string;
31+
PROGRAM_TYPE: "*PGM"|"*SRVPGM";
32+
MODULE_NAME: string;
33+
CLIENT_APPLNAME: string
34+
CLIENT_PROGRAMID: string;
35+
INITIAL_STACK: InitialStackData;
1236
}
1337

1438
export type SelfValue = "*ALL" | "*ERROR" | "*WARNING" | "*NONE";

src/views/jobManager/selfCodes/selfCodesResultsView.ts

Lines changed: 130 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import {
1010
Disposable
1111
} from "vscode";
1212
import { JobManager } from "../../../config";
13-
import { SelfCodeNode } from "./nodes";
13+
import { SelfCodeNode, SelfIleStackFrame } from "./nodes";
14+
import { openExampleCommand } from "../../examples/exampleBrowser";
15+
import { SQLExample } from "../../examples";
1416

1517
type ChangeTreeDataEventType = SelfCodeTreeItem | undefined | null | void;
1618

@@ -42,14 +44,14 @@ export class selfCodesResultsView implements TreeDataProvider<any> {
4244
this.setRefreshEnabled(false, true);
4345
}),
4446
vscode.commands.registerCommand(`vscode-db2i.self.copySqlStatement`, async (item: SelfCodeTreeItem) => {
45-
if (item && item.selfCodeNode.STMTTEXT) {
46-
await vscode.env.clipboard.writeText(item.selfCodeNode.STMTTEXT);
47+
if (item && item.error.STMTTEXT) {
48+
await vscode.env.clipboard.writeText(item.error.STMTTEXT);
4749
vscode.window.showInformationMessage(`SQL statement copied to clipboard.`);
4850
}
4951
}),
5052
vscode.commands.registerCommand(`vscode-db2i.self.displayDetails`, async (item: SelfCodeTreeItem) => {
51-
if (item && item.selfCodeNode) {
52-
const jsonData = JSON.stringify(item.selfCodeNode, null, 2);
53+
if (item && item.error) {
54+
const jsonData = JSON.stringify(item.error, null, 2);
5355
const document = await vscode.workspace.openTextDocument({
5456
content: jsonData,
5557
language: `json`
@@ -81,13 +83,18 @@ export class selfCodesResultsView implements TreeDataProvider<any> {
8183
async getSelfCodes(): Promise<SelfCodeNode[]> {
8284
const selected = JobManager.getSelection();
8385
if (selected) {
84-
const content = `SELECT job_name, user_name, reason_code, logged_time, logged_sqlstate, logged_sqlcode, matches, stmttext,
85-
message_text, message_second_level_text
86-
FROM qsys2.sql_error_log, lateral
87-
(select * from TABLE(SYSTOOLS.SQLCODE_INFO(logged_sqlcode)))
86+
const content = `SELECT
87+
job_name, user_name, reason_code, logged_time, logged_sqlstate, logged_sqlcode, matches, stmttext, message_text, message_second_level_text,
88+
program_library, program_name, program_type, module_name, client_applname, client_programid, initial_stack
89+
FROM qsys2.sql_error_log, lateral (select * from TABLE(SYSTOOLS.SQLCODE_INFO(logged_sqlcode)))
8890
where user_name = current_user
8991
order by logged_time desc`;
90-
const data: SelfCodeNode[] = await JobManager.runSQL<SelfCodeNode>(content, undefined);
92+
93+
const data: SelfCodeNode[] = (await JobManager.runSQL<SelfCodeNode>(content)).map((row) => ({
94+
...row,
95+
INITIAL_STACK: JSON.parse(row.INITIAL_STACK as unknown as string)
96+
}));
97+
9198
return data;
9299
}
93100
return;
@@ -100,28 +107,20 @@ export class selfCodesResultsView implements TreeDataProvider<any> {
100107
getTreeItem(element: any): TreeItem | Thenable<TreeItem> {
101108
return element;
102109
}
103-
async getChildren(element?: any): Promise<any[]> {
110+
111+
async getChildren(element?: SelfCodeTreeItem|SelfErrorStackItem): Promise<any[]> {
104112
if (element) {
105-
return [];
113+
if (element instanceof SelfCodeTreeItem) {
114+
return element.getChilden();
115+
} else if (element instanceof SelfErrorStackItem) {
116+
return element.getChildren();
117+
}
106118
} else {
107119
const selfCodes = await this.getSelfCodes();
108120

109121
if (selfCodes) {
110122
return selfCodes.map((error) => {
111-
const label = `${error.LOGGED_SQLSTATE} (${error.LOGGED_SQLCODE}) ${error.REASON_CODE != null ? error.REASON_CODE : ""}`;
112-
const details = `${error.MESSAGE_TEXT}`; // ${error.MATCHES < 100 ? hitsTxt : '💯'.padStart(10, ' ')} 🔥`;
113-
const hoverMessage = new vscode.MarkdownString(
114-
`**SQL Statement💻:** ${error.STMTTEXT}\n\n---\n\n**SQL Job🛠️:** ${error.JOB_NAME}\n\n---\n\n**Occurrences🔥:** ${error.MATCHES}\n\n---\n\n**Details✏️:** ${error.MESSAGE_SECOND_LEVEL_TEXT}`
115-
);
116-
hoverMessage.isTrusted = true;
117-
const treeItem = new SelfCodeTreeItem(
118-
label,
119-
details,
120-
hoverMessage,
121-
vscode.TreeItemCollapsibleState.None,
122-
error
123-
);
124-
treeItem.contextValue = `selfCodeNode`;
123+
const treeItem = new SelfCodeTreeItem(error);
125124
return treeItem;
126125
});
127126
}
@@ -148,24 +147,117 @@ export class selfCodesResultsView implements TreeDataProvider<any> {
148147
}
149148
}
150149
export class SelfCodeTreeItem extends TreeItem {
151-
selfCodeNode: SelfCodeNode;
152-
153150
constructor(
154-
public readonly errorMessage: string,
155-
public readonly details: string,
156-
public readonly hoverMessage: vscode.MarkdownString,
157-
public readonly collapsibleState: vscode.TreeItemCollapsibleState,
158-
error: SelfCodeNode
151+
public error: SelfCodeNode
159152
) {
160-
super(errorMessage, collapsibleState);
161-
this.selfCodeNode = error;
162-
this.tooltip = hoverMessage; // Hover text
163-
this.description = details; // Additional details shown in the tree view
153+
const label = `${error.LOGGED_SQLSTATE} (${error.LOGGED_SQLCODE}) ${error.REASON_CODE != null ? error.REASON_CODE : ""}`;
154+
super(label, vscode.TreeItemCollapsibleState.Collapsed);
155+
156+
const hover = new vscode.MarkdownString(
157+
[
158+
`**💻 SQL Statement:** ${error.STMTTEXT}`,
159+
``, ``,
160+
`---`,
161+
``, ``,
162+
`**🛠️ SQL Job:** ${error.JOB_NAME}`,
163+
``, ``,
164+
`---`,
165+
``, ``,
166+
`**🔥 Occurrences:** ${error.MATCHES}`,
167+
``, ``,
168+
`---`,
169+
``, ``,
170+
`**✏️ Details:** ${error.MESSAGE_SECOND_LEVEL_TEXT}`
171+
].join(`\n`)
172+
);
173+
hover.isTrusted = true;
174+
175+
this.tooltip = hover;
176+
177+
this.description = error.MESSAGE_TEXT; // Additional details shown in the tree view
164178
this.resourceUri = vscode.Uri.from({
165179
scheme: `selfCodeTreeView`,
166180
path: error.MATCHES.toString()
167181
})
182+
168183
this.iconPath = error.LOGGED_SQLCODE < 0 ? new vscode.ThemeIcon(`error`): new vscode.ThemeIcon(`warning`);
184+
this.contextValue = `selfCodeNode`;
185+
}
186+
187+
getChilden(): TreeItem[] {
188+
const validStack = this.error.INITIAL_STACK.initial_stack
189+
.sort((a, b) => a.ORD - b.ORD) // Ord, low to high
190+
.filter((stack) => stack.LIB !== `QSYS`);
191+
192+
const items = [
193+
new SelfErrorStatementItem(this.error.STMTTEXT),
194+
new SelfErrorNodeItem(`Job`, this.error.JOB_NAME),
195+
new SelfErrorNodeItem(`Client Name`, this.error.CLIENT_APPLNAME),
196+
new SelfErrorNodeItem(`Client Program`, this.error.CLIENT_PROGRAMID),
197+
new SelfErrorNodeItem(`Object`, `${this.error.PROGRAM_LIBRARY}/${this.error.PROGRAM_NAME} (${this.error.PROGRAM_TYPE}, ${this.error.MODULE_NAME})`),
198+
]
199+
200+
if (validStack.length > 0) {
201+
items.push(new SelfErrorStackItem(validStack));
202+
}
203+
204+
return items;
205+
}
206+
}
207+
208+
class SelfErrorStatementItem extends TreeItem {
209+
constructor(statement: string) {
210+
super(`Statement`, vscode.TreeItemCollapsibleState.None);
211+
this.iconPath = new vscode.ThemeIcon(`database`);
212+
this.description = statement;
213+
214+
const hoverable = new vscode.MarkdownString();
215+
hoverable.appendCodeblock(statement, `sql`);
216+
this.tooltip = hoverable;
217+
218+
const example: SQLExample = {
219+
name: `Statement`,
220+
content: [statement]
221+
}
222+
223+
this.command = {
224+
command: openExampleCommand,
225+
title: `Open example`,
226+
arguments: [example]
227+
};
228+
}
229+
}
230+
231+
class SelfErrorNodeItem extends TreeItem {
232+
constructor(label: string, description: string) {
233+
super(label, vscode.TreeItemCollapsibleState.None);
234+
this.iconPath = new vscode.ThemeIcon(`info`);
235+
this.description = description;
236+
}
237+
}
238+
239+
class SelfErrorStackItem extends TreeItem {
240+
constructor(private stack: SelfIleStackFrame[]) {
241+
super(`Stack`, vscode.TreeItemCollapsibleState.Collapsed);
242+
this.iconPath = new vscode.ThemeIcon(`debug`);
243+
this.contextValue = `selfCodeStack`;
244+
245+
this.resourceUri = vscode.Uri.from({
246+
scheme: `selfCodeTreeView`,
247+
path: stack.length.toString()
248+
})
249+
}
250+
251+
getChildren(): SelfErrorStackFrameItem[] {
252+
return this.stack.map((stackCall) => new SelfErrorStackFrameItem(stackCall));
253+
}
254+
}
255+
256+
class SelfErrorStackFrameItem extends TreeItem {
257+
constructor(stackCall: SelfIleStackFrame) {
258+
super(`${stackCall.PROC}:${stackCall.STMT}`, vscode.TreeItemCollapsibleState.None);
259+
this.description = `${stackCall.LIB}/${stackCall.PGM} (${stackCall.TYPE}, ${stackCall.MODULE})`;
260+
this.contextValue = `selfCodeStackCall`;
169261
}
170262
}
171263

@@ -185,8 +277,7 @@ export class SelfTreeDecorationProvider implements FileDecorationProvider {
185277

186278
if (!isNaN(errorCount) && errorCount > 0) {
187279
return {
188-
badge: errorCount < 100 ? errorCount.toString() : '💯',
189-
tooltip: `Occurrences: ${errorCount}`
280+
badge: errorCount < 100 ? errorCount.toString() : '💯'
190281
}
191282
}
192283
}

0 commit comments

Comments
 (0)