From 7f22262d5d49a0b6f7316fe4f24d36981906ebe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Frode=20H=C3=A5skjold?= Date: Fri, 2 May 2025 19:39:39 +0200 Subject: [PATCH 1/5] fix: RPG symbol name using name or label. Remove non-alphanum chars --- package-lock.json | 4 ++-- src/views/results/codegen.test.ts | 15 ++++++++++++++- src/views/results/codegen.ts | 17 +++++++++++++++-- src/views/results/index.ts | 3 ++- 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index f3db529f..ec756446 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscode-db2i", - "version": "1.11.0", + "version": "1.12.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vscode-db2i", - "version": "1.11.0", + "version": "1.12.0", "dependencies": { "@ibm/mapepire-js": "^0.5.0", "@octokit/rest": "^21.1.1", diff --git a/src/views/results/codegen.test.ts b/src/views/results/codegen.test.ts index 375c8ac6..3a518707 100644 --- a/src/views/results/codegen.test.ts +++ b/src/views/results/codegen.test.ts @@ -1,7 +1,20 @@ import { assert, expect, test } from 'vitest' -import { columnToRpgDefinition, queryResultToRpgDs } from './codegen'; +import { columnToRpgDefinition, columnToRpgFieldName, queryResultToRpgDs } from './codegen'; import { QueryResult } from '@ibm/mapepire-js'; +test('Column to RPG symbol', () => { + let name; + + name = columnToRpgFieldName({display_size: 0, label: 'änderungs- benutzer ', name: 'ANDBEN', type: 'CHAR', precision: 10, scale: 0}, 'Label'); + expect(name).toBe('anderungs_benutzer'); + + name = columnToRpgFieldName({display_size: 0, label: 'änderungs- benutzer ', name: 'ANDBEN', type: 'CHAR', precision: 10, scale: 0}, 'Name'); + expect(name).toBe('andben'); + + name = columnToRpgFieldName({display_size: 0, label: '', name: '0001', type: 'INTEGER', precision: 0, scale: 0}, 'Name'); + expect(name).toBe('col0001'); +}); + test('Column to RPG definition', () => { let rpgdef; diff --git a/src/views/results/codegen.ts b/src/views/results/codegen.ts index 9b488059..fc24a174 100644 --- a/src/views/results/codegen.ts +++ b/src/views/results/codegen.ts @@ -1,15 +1,28 @@ import { ColumnMetaData, QueryResult } from "@ibm/mapepire-js"; -export function queryResultToRpgDs(result: QueryResult) : string { +export function queryResultToRpgDs(result: QueryResult, source: string = 'Name') : string { let content = `dcl-ds row_t qualified template;\n`; for (let i = 0; i < result.metadata.column_count; i++) { - const name = `${isNaN(+result.metadata.columns[i].label.charAt(0)) ? '' : 'col'}${result.metadata.columns[i].label.toLowerCase()}` + const name = columnToRpgFieldName(result.metadata.columns[i], source); content += ` ${name} ${columnToRpgDefinition(result.metadata.columns[i])};\n`; } content += `end-ds;\n`; return content; } +export function columnToRpgFieldName(column: ColumnMetaData, source: string = 'Name') : string { + let name = source === 'Label' ? column.label.toLowerCase().trim() : column.name.toLowerCase().trim(); + name = name.replace(/\u00fc/g, "u"); // ü + name = name.replace(/\u00e4/g, "a"); // ä + name = name.replace(/\u00e4/g, "o"); // ö + name = name.replace(/\u00df/g, "s"); // sharp s/Eszett + name = name.replace(/\s+/g, "_").replace(/[^a-zA-Z0-9_]/g, '').trim(); // space to underscore and remove non-alphanumeric chars + if (!isNaN(+name.charAt(0))) { + name = `col` + name; + } + return name; +} + export function columnToRpgDefinition(column: ColumnMetaData) : string { switch (column.type) { case `NUMERIC`: diff --git a/src/views/results/index.ts b/src/views/results/index.ts index 82837aa1..fda2fea2 100644 --- a/src/views/results/index.ts +++ b/src/views/results/index.ts @@ -18,6 +18,7 @@ import { updateStatusBar } from "../jobManager/statusBar"; import { DbCache } from "../../language/providers/logic/cache"; import { ExplainType } from "../../connection/types"; import { queryResultToRpgDs } from "./codegen"; +import Configuration from "../../configuration"; export type StatementQualifier = "statement" | "update" | "explain" | "onlyexplain" | "json" | "csv" | "cl" | "sql" | "rpg"; @@ -390,7 +391,7 @@ async function runHandler(options?: StatementInfo) { let content = `**free\n\n` + `// statement: ${statementDetail.content}\n\n` + `// Row data structure\n` - + queryResultToRpgDs(result); + + queryResultToRpgDs(result, Configuration.get(`resultsets.columnHeadings`)); const textDoc = await vscode.workspace.openTextDocument({ language: 'rpgle', content }); await vscode.window.showTextDocument(textDoc); chosenView.setLoadingText(`RPG data structure generated.`, false); From 65406567c135802e9d3e37ab04f655e7d28315f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Frode=20H=C3=A5skjold?= Date: Fri, 2 May 2025 23:23:49 +0200 Subject: [PATCH 2/5] Replace certain characters + whitespaces in RPG symbol name --- src/views/results/codegen.test.ts | 9 +++++++++ src/views/results/codegen.ts | 17 ++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/views/results/codegen.test.ts b/src/views/results/codegen.test.ts index 3a518707..d1d0952c 100644 --- a/src/views/results/codegen.test.ts +++ b/src/views/results/codegen.test.ts @@ -11,6 +11,15 @@ test('Column to RPG symbol', () => { name = columnToRpgFieldName({display_size: 0, label: 'änderungs- benutzer ', name: 'ANDBEN', type: 'CHAR', precision: 10, scale: 0}, 'Name'); expect(name).toBe('andben'); + name = columnToRpgFieldName({display_size: 0, label: 'Cust.number....:', name: 'ANDBEN', type: 'CHAR', precision: 10, scale: 0}, 'Label'); + expect(name).toBe('cust_number'); + + name = columnToRpgFieldName({display_size: 0, label: 'Country:', name: 'ANDBEN', type: 'CHAR', precision: 10, scale: 0}, 'Label'); + expect(name).toBe('country'); + + name = columnToRpgFieldName({display_size: 0, label: 'På bærtur', name: 'ANDBEN', type: 'CHAR', precision: 10, scale: 0}, 'Label'); + expect(name).toBe('paa_baertur'); + name = columnToRpgFieldName({display_size: 0, label: '', name: '0001', type: 'INTEGER', precision: 0, scale: 0}, 'Name'); expect(name).toBe('col0001'); }); diff --git a/src/views/results/codegen.ts b/src/views/results/codegen.ts index fc24a174..1db106a2 100644 --- a/src/views/results/codegen.ts +++ b/src/views/results/codegen.ts @@ -12,11 +12,18 @@ export function queryResultToRpgDs(result: QueryResult, source: string = 'N export function columnToRpgFieldName(column: ColumnMetaData, source: string = 'Name') : string { let name = source === 'Label' ? column.label.toLowerCase().trim() : column.name.toLowerCase().trim(); - name = name.replace(/\u00fc/g, "u"); // ü - name = name.replace(/\u00e4/g, "a"); // ä - name = name.replace(/\u00e4/g, "o"); // ö - name = name.replace(/\u00df/g, "s"); // sharp s/Eszett - name = name.replace(/\s+/g, "_").replace(/[^a-zA-Z0-9_]/g, '').trim(); // space to underscore and remove non-alphanumeric chars + name = name.replace(/\u00fc/g, "u") // ü -> u + .replace(/\u00e4/g, "a") // ä -> a + .replace(/\u00e4/g, "o") // ö -> o + .replace(/\u00df/g, "s") // sharp s/Eszett -> s + .replace(/\u00e6/g, "ae") // æ -> ae + .replace(/\u00f8/g, "oe") // ø -> oe + .replace(/\u00e5/g, "aa") // å -> aa + .replace(/[.:]+$/g, "") // remove trailing "." and ":" + .replace(/[.]/g, "_") // "." between words to underscore + .replace(/\s+/g, "_") // remaining whitespaces to underscore + .replace(/[^a-zA-Z0-9_]/g, '') // remove non-alphanumeric chars + .trim(); if (!isNaN(+name.charAt(0))) { name = `col` + name; } From 572a53acdbd2be8ba09190a87a2630a56f768491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Frode=20H=C3=A5skjold?= Date: Mon, 5 May 2025 16:29:35 +0200 Subject: [PATCH 3/5] Add configuration for RPG symbolic name source (name/label) --- package.json | 20 ++++++++++++++++++++ src/views/results/contributes.json | 20 ++++++++++++++++++++ src/views/results/index.ts | 2 +- 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 45eecc49..6a0b65ea 100644 --- a/package.json +++ b/package.json @@ -135,6 +135,26 @@ } } }, + { + "id": "vscode-db2i.codegen", + "title": "Code generation", + "properties": { + "vscode-db2i.codegen.rpgSymbolicNameSource": { + "type": "string", + "order": 0, + "description": "Descriptor to use for RPG symbolic names in data structure", + "default": "Name", + "enum": [ + "Name", + "Label" + ], + "enumDescriptions": [ + "Use the column name", + "Use the column label\n'Extended metadata' must be set to true in the JDBC configuration" + ] + } + } + }, { "id": "vscode-db2i.visualExplain", "title": "Visual Explain", diff --git a/src/views/results/contributes.json b/src/views/results/contributes.json index 9f4f529d..9cbb4c69 100644 --- a/src/views/results/contributes.json +++ b/src/views/results/contributes.json @@ -25,6 +25,26 @@ "default": false } } + }, + { + "id": "vscode-db2i.codegen", + "title": "Code generation", + "properties": { + "vscode-db2i.codegen.rpgSymbolicNameSource": { + "type": "string", + "order": 0, + "description": "Descriptor to use for RPG symbolic names in data structure", + "default": "Name", + "enum": [ + "Name", + "Label" + ], + "enumDescriptions": [ + "Use the column name", + "Use the column label\n'Extended metadata' must be set to true in the JDBC configuration" + ] + } + } } ], "views": { diff --git a/src/views/results/index.ts b/src/views/results/index.ts index fda2fea2..28160a5e 100644 --- a/src/views/results/index.ts +++ b/src/views/results/index.ts @@ -391,7 +391,7 @@ async function runHandler(options?: StatementInfo) { let content = `**free\n\n` + `// statement: ${statementDetail.content}\n\n` + `// Row data structure\n` - + queryResultToRpgDs(result, Configuration.get(`resultsets.columnHeadings`)); + + queryResultToRpgDs(result, Configuration.get(`codegen.rpgSymbolicNameSource`)); const textDoc = await vscode.workspace.openTextDocument({ language: 'rpgle', content }); await vscode.window.showTextDocument(textDoc); chosenView.setLoadingText(`RPG data structure generated.`, false); From 1861bae54f8364a4141fad9c4519789b7a20b2a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Frode=20H=C3=A5skjold?= Date: Mon, 5 May 2025 16:58:03 +0200 Subject: [PATCH 4/5] fix typo in columnToRpgFieldName and test --- src/views/results/codegen.test.ts | 7 +++++-- src/views/results/codegen.ts | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/views/results/codegen.test.ts b/src/views/results/codegen.test.ts index d1d0952c..c17efc20 100644 --- a/src/views/results/codegen.test.ts +++ b/src/views/results/codegen.test.ts @@ -14,12 +14,15 @@ test('Column to RPG symbol', () => { name = columnToRpgFieldName({display_size: 0, label: 'Cust.number....:', name: 'ANDBEN', type: 'CHAR', precision: 10, scale: 0}, 'Label'); expect(name).toBe('cust_number'); - name = columnToRpgFieldName({display_size: 0, label: 'Country:', name: 'ANDBEN', type: 'CHAR', precision: 10, scale: 0}, 'Label'); + name = columnToRpgFieldName({display_size: 0, label: 'Country:', name: 'C1', type: 'CHAR', precision: 10, scale: 0}, 'Label'); expect(name).toBe('country'); - name = columnToRpgFieldName({display_size: 0, label: 'På bærtur', name: 'ANDBEN', type: 'CHAR', precision: 10, scale: 0}, 'Label'); + name = columnToRpgFieldName({display_size: 0, label: 'På bærtur', name: 'PB1', type: 'CHAR', precision: 10, scale: 0}, 'Label'); expect(name).toBe('paa_baertur'); + name = columnToRpgFieldName({display_size: 0, label: 'öäß', name: 'ABCD', type: 'CHAR', precision: 10, scale: 0}, 'Label'); + expect(name).toBe('oas'); + name = columnToRpgFieldName({display_size: 0, label: '', name: '0001', type: 'INTEGER', precision: 0, scale: 0}, 'Name'); expect(name).toBe('col0001'); }); diff --git a/src/views/results/codegen.ts b/src/views/results/codegen.ts index 1db106a2..4242710f 100644 --- a/src/views/results/codegen.ts +++ b/src/views/results/codegen.ts @@ -14,7 +14,7 @@ export function columnToRpgFieldName(column: ColumnMetaData, source: string = 'N let name = source === 'Label' ? column.label.toLowerCase().trim() : column.name.toLowerCase().trim(); name = name.replace(/\u00fc/g, "u") // ü -> u .replace(/\u00e4/g, "a") // ä -> a - .replace(/\u00e4/g, "o") // ö -> o + .replace(/\u00f6/g, "o") // ö -> o .replace(/\u00df/g, "s") // sharp s/Eszett -> s .replace(/\u00e6/g, "ae") // æ -> ae .replace(/\u00f8/g, "oe") // ø -> oe From a121713fe529742e40e9840f9fc428803998f03d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Frode=20H=C3=A5skjold?= Date: Mon, 5 May 2025 21:37:33 +0200 Subject: [PATCH 5/5] Fix for whitespace in label --- src/views/results/codegen.test.ts | 5 ++++- src/views/results/codegen.ts | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/views/results/codegen.test.ts b/src/views/results/codegen.test.ts index c17efc20..a3e858f4 100644 --- a/src/views/results/codegen.test.ts +++ b/src/views/results/codegen.test.ts @@ -11,9 +11,12 @@ test('Column to RPG symbol', () => { name = columnToRpgFieldName({display_size: 0, label: 'änderungs- benutzer ', name: 'ANDBEN', type: 'CHAR', precision: 10, scale: 0}, 'Name'); expect(name).toBe('andben'); - name = columnToRpgFieldName({display_size: 0, label: 'Cust.number....:', name: 'ANDBEN', type: 'CHAR', precision: 10, scale: 0}, 'Label'); + name = columnToRpgFieldName({display_size: 0, label: 'Cust.number....:', name: 'CUSNUM', type: 'CHAR', precision: 10, scale: 0}, 'Label'); expect(name).toBe('cust_number'); + name = columnToRpgFieldName({display_size: 0, label: 'Cust. name.... : ', name: 'CUSNAM', type: 'CHAR', precision: 10, scale: 0}, 'Label'); + expect(name).toBe('cust_name'); + name = columnToRpgFieldName({display_size: 0, label: 'Country:', name: 'C1', type: 'CHAR', precision: 10, scale: 0}, 'Label'); expect(name).toBe('country'); diff --git a/src/views/results/codegen.ts b/src/views/results/codegen.ts index 4242710f..b90ceb75 100644 --- a/src/views/results/codegen.ts +++ b/src/views/results/codegen.ts @@ -19,10 +19,11 @@ export function columnToRpgFieldName(column: ColumnMetaData, source: string = 'N .replace(/\u00e6/g, "ae") // æ -> ae .replace(/\u00f8/g, "oe") // ø -> oe .replace(/\u00e5/g, "aa") // å -> aa - .replace(/[.:]+$/g, "") // remove trailing "." and ":" + .replace(/[ .:]+$/g, "") // remove trailing space, "." and ":" .replace(/[.]/g, "_") // "." between words to underscore .replace(/\s+/g, "_") // remaining whitespaces to underscore - .replace(/[^a-zA-Z0-9_]/g, '') // remove non-alphanumeric chars + .replace(/[^a-zA-Z0-9_]/g, "") // remove non-alphanumeric chars + .replace(/\_+/i, "_") // replace multiple underscores with single underscore .trim(); if (!isNaN(+name.charAt(0))) { name = `col` + name;