Skip to content

Commit ac4c7d6

Browse files
committed
Better support for CTE references
Signed-off-by: worksofliam <[email protected]>
1 parent f60ff47 commit ac4c7d6

File tree

2 files changed

+67
-20
lines changed

2 files changed

+67
-20
lines changed

src/language/sql/statement.ts

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@ export default class Statement {
2626
}
2727

2828
switch (this.type) {
29-
case StatementType.With:
30-
this.tokens = SQLTokeniser.createBlocks(this.tokens);
31-
break;
3229
case StatementType.Create:
3330
// No scalar transformation here..
3431
break;
@@ -213,23 +210,25 @@ export default class Statement {
213210

214211
getCTEReferences(): CTEReference[] {
215212
if (this.type !== StatementType.With) return [];
213+
214+
const withBlocks = SQLTokeniser.createBlocks(this.tokens.slice(0));
216215

217216
let cteList: CTEReference[] = [];
218217

219-
for (let i = 0; i < this.tokens.length; i++) {
220-
if (tokenIs(this.tokens[i], `word`)) {
221-
let cteName = this.tokens[i].value!;
218+
for (let i = 0; i < withBlocks.length; i++) {
219+
if (tokenIs(withBlocks[i], `word`) || tokenIs(withBlocks[i], `function`)) {
220+
let cteName = withBlocks[i].value!;
222221
let parameters: string[] = [];
223222
let statementBlockI = i+1;
224223

225-
if (tokenIs(this.tokens[i+1], `block`) && tokenIs(this.tokens[i+2], `keyword`, `AS`)) {
226-
parameters = this.tokens[i+1].block!.filter(blockToken => blockToken.type === `word`).map(blockToken => blockToken.value!)
224+
if (tokenIs(withBlocks[i+1], `block`) && tokenIs(withBlocks[i+2], `keyword`, `AS`)) {
225+
parameters = withBlocks[i+1].block!.filter(blockToken => blockToken.type === `word`).map(blockToken => blockToken.value!)
227226
statementBlockI = i+3;
228-
} else if (tokenIs(this.tokens[i+1], `keyword`, `AS`)) {
227+
} else if (tokenIs(withBlocks[i+1], `keyword`, `AS`)) {
229228
statementBlockI = i+2;
230229
}
231230

232-
const statementBlock = this.tokens[statementBlockI];
231+
const statementBlock = withBlocks[statementBlockI];
233232
if (tokenIs(statementBlock, `block`)) {
234233
cteList.push({
235234
name: cteName,
@@ -241,7 +240,7 @@ export default class Statement {
241240
i = statementBlockI;
242241
}
243242

244-
if (tokenIs(this.tokens[i], `statementType`, `SELECT`)) {
243+
if (tokenIs(withBlocks[i], `statementType`, `SELECT`)) {
245244
break;
246245
}
247246
}
@@ -513,11 +512,8 @@ export default class Statement {
513512

514513
let endIndex = i;
515514

516-
let isUDTF = false;
515+
let isUDTF = tokenIs(nextToken, `function`, `TABLE`);
517516

518-
if (tokenIs(nextToken, `function`, `TABLE`)) {
519-
isUDTF = true;
520-
}
521517

522518
if (isUDTF) {
523519
sqlObj = this.getRefAtToken(i+2);

src/language/sql/tests/statements.test.ts

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -943,6 +943,35 @@ describe(`Object references`, () => {
943943
expect(refs[0].object.name).toBe(`apiVersion`);
944944
expect(refs[0].createType).toBe(`varchar(10) ccsid 1208 default '2023-07-07'`);
945945
});
946+
947+
test(`SELECT, WITH & LATERAL`, () => {
948+
const lines = [
949+
`with qsysobjs (lib, obj, type) as (`,
950+
` select object_library, object_name, object_type`,
951+
` from table (qsys2.object_ownership(current_user))`,
952+
` where path_name is null`,
953+
`)`,
954+
`select lib concat '/' concat obj concat ' (' concat type concat ')' as label,`,
955+
` objsize as "Size"`,
956+
` from qsysobjs q, lateral (`,
957+
` select objcreated, last_used_timestamp, objsize`,
958+
` from table (qsys2.object_statistics(lib, type, obj))`,
959+
` ) z`,
960+
`where objsize is not null`,
961+
`order by OBJSIZE DESC`,
962+
`limit 10;`,
963+
].join(`\n`);
964+
965+
const document = new Document(lines);
966+
967+
expect(document.statements.length).toBe(1);
968+
969+
const statement = document.statements[0];
970+
971+
expect(statement.type).toBe(StatementType.With);
972+
973+
const refs = statement.getObjectReferences();
974+
});
946975
});
947976

948977
describe(`Offset reference tests`, () => {
@@ -1138,8 +1167,28 @@ describe(`PL body tests`, () => {
11381167
const refs = statement.getObjectReferences();
11391168
const ctes = statement.getCTEReferences();
11401169

1141-
expect(refs.length).toBe(1);
1142-
expect(refs[0].object.name).toBe(`Temp02`);
1170+
console.log(refs);
1171+
expect(refs.length).toBe(7);
1172+
expect(refs[0].object.name).toBe(`shipments`);
1173+
expect(refs[0].alias).toBe(`s`);
1174+
1175+
expect(refs[1].object.name).toBe(`BillingDate`);
1176+
expect(refs[1].alias).toBeUndefined();
1177+
1178+
expect(refs[2].object.name).toBe(`Temp01`);
1179+
expect(refs[2].alias).toBe(`t1`);
1180+
1181+
expect(refs[3].object.name).toBe(`Temp01`);
1182+
expect(refs[3].alias).toBe(`t1`);
1183+
1184+
expect(refs[4].object.name).toBe(`Temp02`);
1185+
expect(refs[4].alias).toBe(`t2`);
1186+
1187+
expect(refs[5].object.name).toBe(`customers`);
1188+
expect(refs[5].alias).toBe(`c`);
1189+
1190+
expect(refs[6].object.name).toBe(`Temp02`);
1191+
expect(refs[6].alias).toBeUndefined();
11431192

11441193
expect(ctes.length).toBe(3);
11451194
expect(ctes[0].name).toBe(`Temp01`);
@@ -1174,9 +1223,11 @@ describe(`PL body tests`, () => {
11741223
expect(statement.type).toBe(StatementType.With);
11751224

11761225
const objs = statement.getObjectReferences();
1177-
expect(objs.length).toBe(1);
1178-
expect(objs[0].object.schema).toBe(undefined);
1179-
expect(objs[0].object.name).toBe(`cteme`);
1226+
expect(objs.length).toBe(2);
1227+
expect(objs[0].object.schema).toBe(`qsys2`);
1228+
expect(objs[0].object.name).toBe(`sysixadv`);
1229+
expect(objs[1].object.schema).toBe(undefined);
1230+
expect(objs[1].object.name).toBe(`cteme`);
11801231

11811232
const ctes = statement.getCTEReferences();
11821233
expect(ctes.length).toBe(1);

0 commit comments

Comments
 (0)