Skip to content

Commit 7108910

Browse files
authored
Merge pull request #59 from IBM/fix/long_name_lookups
Fix/long_name_lookups
2 parents 76c6b57 + 50adafa commit 7108910

File tree

7 files changed

+291
-7
lines changed

7 files changed

+291
-7
lines changed

cli/package-lock.json

+4-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/src/targets.ts

+62-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import glob from 'glob';
22
import path from 'path';
33
import fs from 'fs/promises';
4+
import fss from 'fs';
45
import Cache from "vscode-rpgle/language/models/cache";
56
import { IncludeStatement } from "vscode-rpgle/language/parserTypes";
67
import { infoOut, warningOut } from './cli';
78
import { DefinitionType, File, Module, CLParser } from 'vscode-clle/language';
89
import { DisplayFile as dds } from "vscode-displayfile/src/dspf";
910
import Document from "vscode-db2i/src/language/sql/document";
10-
import { StatementType } from 'vscode-db2i/src/language/sql/types';
11+
import { ObjectRef, StatementType } from 'vscode-db2i/src/language/sql/types';
1112
import { rpgExtensions, clExtensions, ddsExtension, sqlExtensions, srvPgmExtensions, cmdExtensions } from './extensions';
1213
import Parser from "vscode-rpgle/language/parser";
1314
import { setupParser } from './parser';
@@ -148,6 +149,16 @@ export class Targets {
148149
extension
149150
};
150151

152+
// If this file is an SQL file, we need to look to see if it has a long name as we need to resolve all names here
153+
if (sqlExtensions.includes(extension.toLowerCase())) {
154+
const ref = this.sqlObjectDataFromPath(localPath);
155+
if (ref) {
156+
if (ref.object.system) theObject.systemName = ref.object.system.toUpperCase();
157+
if (ref.object.name) theObject.longName = ref.object.name;
158+
// theObject.type = ref.type;
159+
}
160+
}
161+
151162
this.storeResolved(localPath, theObject);
152163

153164
return theObject;
@@ -224,7 +235,7 @@ export class Targets {
224235
* Resolves a search to an object. Use `systemName` parameter for short and long name.
225236
*/
226237
public searchForObject(lookFor: ILEObject) {
227-
return this.getResolvedObjects().find(o => (o.systemName === lookFor.systemName || o.systemName === lookFor.longName) && o.type === lookFor.type);
238+
return this.getResolvedObjects().find(o => (lookFor.systemName === o.systemName || (o.longName && lookFor.systemName === o.longName)) && o.type === lookFor.type);
228239
}
229240

230241
public searchForAnyObject(lookFor: { name: string, types: ObjectType[] }) {
@@ -322,6 +333,7 @@ export class Targets {
322333
}
323334

324335
public loadObjectsFromPaths(paths: string[]) {
336+
// optimiseFileList(paths); //Ensure we load SQL files first
325337
paths.forEach(p => this.resolvePathToObject(p));
326338
}
327339

@@ -1202,6 +1214,9 @@ export class Targets {
12021214
.forEach((ref: RpgLookup) => {
12031215
if (ignoredObjects.includes(ref.lookup)) return;
12041216

1217+
const previouslyScanned = target.deps.some((r => (ref.lookup === r.systemName || ref.lookup === r.longName?.toUpperCase()) && r.type === `FILE`));
1218+
if (previouslyScanned) return;
1219+
12051220
const resolvedObject = this.searchForObject({ systemName: ref.lookup, type: `FILE` });
12061221
if (resolvedObject) target.deps.push(resolvedObject)
12071222
else {
@@ -1221,7 +1236,7 @@ export class Targets {
12211236
line: ref.position ? ref.position.line : undefined
12221237
}))
12231238
.forEach((ref: RpgLookup) => {
1224-
const previouslyScanned = target.deps.some((r => r.systemName === ref.lookup && r.type === `FILE`));
1239+
const previouslyScanned = target.deps.some((r => (ref.lookup === r.systemName || ref.lookup === r.longName?.toUpperCase()) && r.type === `FILE`));
12251240
if (previouslyScanned) return;
12261241
const resolvedObject = this.searchForObject({ systemName: ref.lookup, type: `FILE` });
12271242
if (resolvedObject) target.deps.push(resolvedObject)
@@ -1585,6 +1600,50 @@ export class Targets {
15851600
return currentItem;
15861601
}
15871602

1603+
/**
1604+
* This is used when loading in all objects.
1605+
* SQL sources can have two object names: a system name and a long name
1606+
* Sadly the long name is not typically part of the path name, so we need to
1607+
* find the name inside of the source code.
1608+
*/
1609+
sqlObjectDataFromPath(fullPath: string): ObjectRef|undefined {
1610+
const relativePath = this.getRelative(fullPath);
1611+
1612+
if (fss.existsSync(fullPath)) {
1613+
const content = fss.readFileSync(fullPath, { encoding: `utf-8` });
1614+
const document = new Document(content);
1615+
1616+
const groups = document.getStatementGroups();
1617+
1618+
if (groups.length === 0) {
1619+
this.logger.fileLog(relativePath, {
1620+
message: `No SQL statements found in file.`,
1621+
type: `info`
1622+
});
1623+
1624+
return;
1625+
}
1626+
1627+
const createCount = groups.filter(g => g.statements[0].type === StatementType.Create).length;
1628+
1629+
if (createCount > 1) {
1630+
this.logger.fileLog(relativePath, {
1631+
message: `Includes multiple create statements. They should be in individual sources. This file will not be parsed.`,
1632+
type: `warning`,
1633+
});
1634+
}
1635+
1636+
const firstGroup = groups[0];
1637+
const create = firstGroup.statements.find(s => s.type === StatementType.Create);
1638+
1639+
if (create) {
1640+
const defs = create.getObjectReferences();
1641+
const mainDef = defs.find(d => d.createType);
1642+
1643+
return mainDef;
1644+
}
1645+
}
1646+
}
15881647
}
15891648

15901649
function trimQuotes(input: string, value = `'`) {

cli/test/bobLongNames.test.ts

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { assert, beforeAll, describe, expect, test } from 'vitest';
2+
3+
import { Targets } from '../src/targets'
4+
import path from 'path';
5+
import { MakeProject } from '../src/builders/make';
6+
import { getFiles } from '../src/utils';
7+
import { setupFixture } from './fixtures/projects';
8+
import { scanGlob } from '../src/extensions';
9+
import { writeFileSync } from 'fs';
10+
import { BobProject } from '../src';
11+
12+
const cwd = setupFixture(`bob_long_names`);
13+
14+
let files = getFiles(cwd, scanGlob);
15+
16+
describe.skipIf(files.length === 0)(`long name test`, () => {
17+
const targets = new Targets(cwd);
18+
targets.setSuggestions({ renames: true, includes: true })
19+
20+
beforeAll(async () => {
21+
targets.loadObjectsFromPaths(files);
22+
const parsePromises = files.map(f => targets.parseFile(f));
23+
await Promise.all(parsePromises);
24+
25+
expect(targets.getTargets().length).toBeGreaterThan(0);
26+
targets.resolveBinder();
27+
});
28+
29+
test(`Ensure objects are defined`, async () => {
30+
expect(targets.getTargets().length).toBe(1);
31+
expect(targets.getResolvedObjects(`FILE`).length).toBe(1);
32+
expect(targets.binderRequired()).toBe(false);
33+
34+
const dspf = targets.searchForObject({ systemName: `ART301D`, type: `FILE` });
35+
expect(dspf).toBeDefined();
36+
});
37+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
A*%%TS SD 20161129 155718 VTAQUIN REL-V7R1M0 5770-WDS
2+
A*%%EC
3+
A DSPSIZ(24 80 *DS3)
4+
A REF(*LIBL/ARTICLE)
5+
A INDARA
6+
A PRINT
7+
A ERRSFL
8+
A CA03(03)
9+
A CA12(12)
10+
A R SFL01 SFL
11+
A*%%TS SD 20161129 105751 VTAQUIN REL-V7R1M0 5770-WDS
12+
A 33 SFLNXTCHG
13+
A OPT01 1Y 0B 8 3
14+
A 34 DSPATR(RI)
15+
A 34 DSPATR(PC)
16+
A EDTCDE(Z)
17+
A ARID R O 8 5REFFLD(ARID ARTICLE)
18+
A ARDESC R O 8 12REFFLD(FARTI/ARDESC *LIBL/ARTICLE)
19+
A ARTIFA R O 8 63REFFLD(FARTI/ARTIFA *LIBL/ARTICLE)
20+
A ARSALEPR R O 8 67REFFLD(FARTI/ARSALEPR *LIBL/ARTICLE)
21+
A R CTL01 SFLCTL(SFL01)
22+
A*%%TS SD 20161129 155718 VTAQUIN REL-V7R1M0 5770-WDS
23+
A SFLSIZ(0015)
24+
A SFLPAG(0014)
25+
A N80 PAGEDOWN(25 'dynamic subfile')
26+
A CF04(04)
27+
A OVERLAY
28+
A 31 SFLDSP
29+
A 32 SFLDSPCTL
30+
A 30 SFLCLR
31+
A 80 SFLEND(*MORE)
32+
A 35 SFLMSG('INVALID OPTION' 35)
33+
A 36 SFLMSG('ONLY ONE SELECTION' 36)
34+
A RRB01 4S 0H SFLRCDNBR
35+
A 1 32'Select an Article'
36+
A DSPATR(HI)
37+
A 4 3'Make a selection. Press Enter'
38+
A COLOR(BLU)
39+
A 5 5'1=Select'
40+
A COLOR(BLU)
41+
A 7 2'Opt'
42+
A DSPATR(HI)
43+
A 7 6'Code'
44+
A DSPATR(HI)
45+
A 1 2'ART301'
46+
A COLOR(BLU)
47+
A 2 3'Desc contains . . . :'
48+
A 3 3'Family . . . . . . . :'
49+
A SRCHDESC 10A B 2 27
50+
A 7 12'Description'
51+
A DSPATR(HI)
52+
A 7 63'Fam'
53+
A DSPATR(HI)
54+
A 7 68'Price'
55+
A DSPATR(HI)
56+
A SRCHFAM R B 3 27REFFLD(FARTI/ARTIFA *LIBL/ARTICLE)
57+
A 1 70DATE
58+
A EDTCDE(Y)
59+
A COLOR(BLU)
60+
A 2 70TIME
61+
A COLOR(BLU)
62+
A FAMDESC 20A O 3 33
63+
A R KEY01
64+
A*%%TS SD 20161129 155218 VTAQUIN REL-V7R1M0 5770-WDS
65+
A OVERLAY
66+
A 23 2'F3=Exit'
67+
A COLOR(BLU)
68+
A 23 29'F12=Cancel'
69+
A COLOR(BLU)
70+
A 23 14'F4=Prompt'
71+
A COLOR(BLU)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
**free
2+
3+
ctl-opt nomain;
4+
5+
dcl-proc getLiveBalance;
6+
dcl-pi *n likeDs(liveResultT);
7+
type char(1) const;
8+
cusno int(10) const;
9+
end-pi;
10+
11+
dcl-ds liveResult likeds(liveResultT);
12+
13+
dcl-f trans qualified usropn usage(*input);
14+
dcl-ds transaction likerec(trans.transfmt);
15+
16+
liveResult.total = 0;
17+
liveResult.count = 0;
18+
19+
select;
20+
when (type = RLA);
21+
// This RLA check is a bit of a hack.
22+
// In the future, we might want to use
23+
// a keyed field or an LF to look for
24+
// the card by customer id instead of
25+
// manually scanning the file.
26+
27+
// The larger the file, the longer this will take
28+
29+
OPEN trans;
30+
31+
read trans.transfmt transaction;
32+
dow not %eof;
33+
if (transaction.TCUS = cusno);
34+
liveResult.total += transaction.TAMT;
35+
liveResult.count += 1;
36+
endif;
37+
read trans.transfmt transaction;
38+
enddo;
39+
40+
CLOSE trans;
41+
42+
when (type = SQL);
43+
EXEC SQL
44+
SELECT count(*), coalesce(sum(TAMT), 0)
45+
INTO :liveResult.count, :liveResult.total
46+
FROM TRANSACTION
47+
WHERE TCUS = :cusno;
48+
endsl;
49+
50+
return liveResult;
51+
52+
end-proc;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-- https://www.ibm.com/docs/en/i/7.3?topic=tables-employee-table-employee
2+
3+
CREATE OR REPLACE TABLE TRANSACTION FOR SYSTEM NAME TRANS
4+
(TRID integer not null GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1),
5+
TDESC VARCHAR(50) NOT NULL,
6+
TCUS integer NOT NULL, -- reference to customers
7+
TAMT numeric(10,2) NOT NULL default 0,
8+
TWHEN TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
9+
PRIMARY KEY (TRID)) RCDFMT TRANSFMT;

cli/test/sqlLongNames.test.ts

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { assert, beforeAll, describe, expect, test } from 'vitest';
2+
3+
import { Targets } from '../src/targets'
4+
import path from 'path';
5+
import { MakeProject } from '../src/builders/make';
6+
import { getFiles } from '../src/utils';
7+
import { setupFixture } from './fixtures/projects';
8+
import { scanGlob } from '../src/extensions';
9+
10+
const cwd = setupFixture(`sql_long_names`);
11+
12+
// This issue was occuring when you had two files with the same name, but different extensions.
13+
14+
let files = getFiles(cwd, scanGlob);
15+
16+
describe.skipIf(files.length === 0)(`sql long name lookup`, () => {
17+
const targets = new Targets(cwd);
18+
targets.setSuggestions({ renames: true, includes: true })
19+
20+
beforeAll(async () => {
21+
targets.loadObjectsFromPaths(files);
22+
const parsePromises = files.map(f => targets.parseFile(f));
23+
await Promise.all(parsePromises);
24+
25+
expect(targets.getTargets().length).toBeGreaterThan(0);
26+
targets.resolveBinder();
27+
});
28+
29+
test(`Ensure objects are defined`, async () => {
30+
expect(targets.getTargets().length).toBe(2);
31+
expect(targets.getResolvedObjects(`FILE`).length).toBe(1);
32+
expect(targets.getResolvedObjects(`MODULE`).length).toBe(1);
33+
expect(targets.binderRequired()).toBe(false);
34+
35+
const trans = targets.searchForObject({ systemName: `TRANS`, type: `FILE` });
36+
expect(trans).toBeDefined();
37+
38+
const transaction = targets.searchForObject({ systemName: `TRANSACTION`, type: `FILE` });
39+
expect(transaction).toBeDefined();
40+
41+
expect(trans).toMatchObject(transaction);
42+
43+
const moduleLogs = targets.logger.getLogsFor(trans.relativePath);
44+
expect(moduleLogs).toBeUndefined();
45+
});
46+
47+
test(`Ensure deps are correct`, async () => {
48+
const trans = targets.getTarget({ systemName: `DB`, type: `MODULE` });
49+
expect(trans).toBeDefined();
50+
51+
expect(trans.deps.length).toBe(1);
52+
expect(trans.deps[0].systemName).toBe(`TRANS`);
53+
expect(trans.deps[0].longName).toBe(`TRANSACTION`);
54+
});
55+
56+
});

0 commit comments

Comments
 (0)