Skip to content

Commit 5b85d8a

Browse files
authored
Merge pull request #140 from halcyon-tech/feature/multiple_object_types
Feature/multiple_object_types
2 parents 53c4a48 + f9a7921 commit 5b85d8a

File tree

8 files changed

+311
-167
lines changed

8 files changed

+311
-167
lines changed

package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,12 @@
224224
"title": "Set current schema",
225225
"category": "Db2 for i"
226226
},
227+
{
228+
"command": "vscode-db2i.setSchemaFilter",
229+
"title": "Set filter",
230+
"category": "Db2 for i",
231+
"icon": "$(search)"
232+
},
227233
{
228234
"command": "vscode-db2i.pasteGenerator",
229235
"title": "Paste JSON as SQL",
@@ -330,6 +336,10 @@
330336
"command": "vscode-db2i.queryHistory.remove",
331337
"when": "never"
332338
},
339+
{
340+
"command": "vscode-db2i.setSchemaFilter",
341+
"when": "never"
342+
},
333343
{
334344
"command": "vscode-db2i.jobManager.closeJob",
335345
"when": "never"
@@ -434,6 +444,11 @@
434444
"when": "viewItem == schema",
435445
"group": "db2@1"
436446
},
447+
{
448+
"command": "vscode-db2i.setSchemaFilter",
449+
"when": "viewItem == schema",
450+
"group": "inline"
451+
},
437452
{
438453
"command": "vscode-db2i.getResultSet",
439454
"when": "viewItem == table || viewItem == view || viewItem == alias",

src/database/callable.ts

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,37 @@ export default class Callable {
1010
* @param specificName Not user input
1111
* @returns
1212
*/
13-
static getParms(schema: string, specificName: string): Promise<SQLParm[]> {
13+
static getParms(schema: string, specificName: string, resolveName: boolean = false): Promise<SQLParm[]> {
1414
const rowType = `P`; // Parameter
15-
const options : QueryOptions = { parameters : [schema, schema, specificName, rowType] };
16-
return JobManager.runSQL<SQLParm>([
17-
`SELECT * FROM QSYS2.SYSPARMS`,
18-
`WHERE SPECIFIC_SCHEMA = ? AND SPECIFIC_NAME = (
19-
select specific_name from qsys2.sysroutines where specific_schema = ? and routine_name = ?
20-
) and ROW_TYPE = ?`,
21-
`ORDER BY ORDINAL_POSITION`
22-
].join(` `),
23-
options);
15+
return Callable.getFromSysParms(schema, specificName, rowType, resolveName);
2416
}
2517

26-
static getResultColumns(schema: string, specificName: string): Promise<SQLParm[]> {
18+
static getResultColumns(schema: string, specificName: string, resolveName: boolean = false) {
2719
const rowType = `R`; // Row
28-
const options : QueryOptions = { parameters : [schema, schema, specificName, rowType] };
29-
return JobManager.runSQL<SQLParm>([
30-
`SELECT * FROM QSYS2.SYSPARMS`,
31-
`WHERE SPECIFIC_SCHEMA = ? AND SPECIFIC_NAME = (
32-
select specific_name from qsys2.sysroutines where specific_schema = ? and routine_name = ?
33-
) and ROW_TYPE = ?`,
34-
`ORDER BY ORDINAL_POSITION`
35-
].join(` `),
36-
options);
20+
return Callable.getFromSysParms(schema, specificName, rowType, resolveName);
21+
}
22+
23+
static getFromSysParms(schema: string, name: string, rowType: "P"|"R", resolveName: boolean = false): Promise<SQLParm[]> {
24+
let parameters = [schema, rowType];
25+
26+
let specificNameClause = undefined;
27+
28+
if (resolveName) {
29+
specificNameClause = `SPECIFIC_NAME = (select specific_name from qsys2.sysroutines where specific_schema = ? and routine_name = ?)`;
30+
parameters.push(schema, name);
31+
} else {
32+
specificNameClause = `SPECIFIC_NAME = ?`;
33+
parameters.push(name);
34+
}
35+
36+
const options : QueryOptions = { parameters };
37+
return JobManager.runSQL<SQLParm>(
38+
[
39+
`SELECT * FROM QSYS2.SYSPARMS`,
40+
`WHERE SPECIFIC_SCHEMA = ? AND ROW_TYPE = ? AND ${specificNameClause}`,
41+
`ORDER BY ORDINAL_POSITION`
42+
].join(` `),
43+
options
44+
);
3745
}
3846
}

src/database/schemas.ts

Lines changed: 127 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -13,132 +13,150 @@ const typeMap = {
1313
'aliases': [`A`]
1414
};
1515

16+
export const AllSQLTypes: SQLType[] = ["tables", "views", "aliases", "constraints", "functions", "variables", "indexes", "procedures", "sequences", "packages", "triggers", "types"];
17+
1618
export default class Schemas {
1719
/**
1820
* @param schema Not user input
1921
*/
20-
static async getObjects(schema: string, type: SQLType, details: PageData = {}): Promise<BasicSQLObject[]> {
22+
static async getObjects(schema: string, types: SQLType[], details: PageData = {}): Promise<BasicSQLObject[]> {
23+
let selects: string[] = [];
2124
let query : string;
22-
switch (type) {
23-
case `schemas`:
24-
query = [
25-
`select SCHEMA_NAME as NAME, SCHEMA_TEXT as TEXT, SYSTEM_SCHEMA_NAME as SYS_NAME `,
26-
`from QSYS2.SYSSCHEMAS`,
27-
details.filter ? `where SCHEMA_NAME = '${details.filter}' or SYSTEM_SCHEMA_NAME = '${details.filter}'` : ``,
28-
`order by QSYS2.DELIMIT_NAME(SCHEMA_NAME) asc`
29-
].join(` `);
30-
break;
31-
case `tables`:
32-
case `views`:
33-
case `aliases`:
34-
query = [
35-
`select TABLE_NAME as NAME, TABLE_TEXT as TEXT, BASE_TABLE_SCHEMA as BASE_SCHEMA, BASE_TABLE_NAME as BASE_OBJ, SYSTEM_TABLE_NAME as SYS_NAME, SYSTEM_TABLE_SCHEMA as SYS_SCHEMA `,
36-
`from QSYS2.SYSTABLES`,
37-
`where TABLE_SCHEMA = '${schema}' and TABLE_TYPE in (${typeMap[type].map(item => `'${item}'`).join(`, `)}) ${details.filter ? `and TABLE_NAME like '%${details.filter}%'`: ``}`,
38-
`order by TABLE_NAME asc`
39-
].join(` `);
40-
break;
41-
42-
case `constraints`:
43-
query = [
44-
`select CONSTRAINT_NAME as NAME, CONSTRAINT_TEXT as TEXT, TABLE_SCHEMA as BASE_SCHEMA, TABLE_NAME as BASE_OBJ, SYSTEM_TABLE_NAME as SYS_NAME, SYSTEM_TABLE_SCHEMA as SYS_SCHEMA `,
45-
`from QSYS2.SYSCST`,
46-
`where CONSTRAINT_SCHEMA = '${schema}' ${details.filter ? `and CONSTRAINT_NAME like '%${details.filter}%'`: ``}`,
47-
`order by CONSTRAINT_NAME asc`
48-
].join(` `);
49-
break;
50-
51-
case `functions`:
52-
query = [
53-
`select ROUTINE_NAME as NAME, SPECIFIC_NAME as SPECNAME, coalesce(ROUTINE_TEXT, LONG_COMMENT) as TEXT `,
54-
`from QSYS2.SYSFUNCS`,
55-
`where ROUTINE_SCHEMA = '${schema}' ${details.filter ? `and ROUTINE_NAME like '%${details.filter}%'`: ``} and FUNCTION_ORIGIN in ('E','U')`,
56-
`order by ROUTINE_NAME asc`
57-
].join(` `);
58-
break;
59-
60-
case `variables`:
61-
query = [
62-
`select VARIABLE_NAME as NAME, VARIABLE_TEXT as TEXT, SYSTEM_VAR_NAME as SYS_NAME, SYSTEM_VAR_SCHEMA as SYS_SCHEMA `,
63-
`from QSYS2.SYSVARIABLES`,
64-
`where VARIABLE_SCHEMA = '${schema}' ${details.filter ? `and VARIABLE_NAME like '%${details.filter}%'`: ``}`,
65-
`order by VARIABLE_NAME asc`
66-
].join(` `);
67-
break;
68-
69-
case `indexes`:
70-
query = [
71-
`select INDEX_NAME as NAME, INDEX_TEXT as TEXT, TABLE_SCHEMA as BASE_SCHEMA, TABLE_NAME as BASE_OBJ, SYSTEM_INDEX_NAME as SYS_NAME, SYSTEM_INDEX_SCHEMA as SYS_SCHEMA `,
72-
`from QSYS2.SYSINDEXES`,
73-
`where INDEX_SCHEMA = '${schema}' ${details.filter ? `and INDEX_NAME like '%${details.filter}%'`: ``}`,
74-
`order by INDEX_NAME asc`
75-
].join(` `);
76-
break;
77-
78-
case `procedures`:
79-
query = [
80-
`select ROUTINE_NAME as NAME, SPECIFIC_NAME as SPECNAME, ROUTINE_TEXT as TEXT `,
81-
`from QSYS2.SYSPROCS`,
82-
`where ROUTINE_SCHEMA = '${schema}' ${details.filter ? `and ROUTINE_NAME like '%${details.filter}%'`: ``}`,
83-
`order by ROUTINE_NAME asc`
84-
].join(` `);
85-
break;
86-
87-
case `sequences`:
88-
query = [
89-
`select SEQUENCE_NAME as NAME, SEQUENCE_TEXT as TEXT, SYSTEM_SEQ_NAME as SYS_NAME, SYSTEM_SEQ_SCHEMA as SYS_SCHEMA `,
90-
`from QSYS2.SYSSEQUENCES`,
91-
`where SEQUENCE_SCHEMA = '${schema}' ${details.filter ? `and SEQUENCE_NAME like '%${details.filter}%'`: ``}`,
92-
`order by SEQUENCE_NAME asc`
93-
].join(` `);
94-
break;
95-
96-
case `packages`:
97-
query = [
98-
`select PACKAGE_NAME as NAME, PACKAGE_TEXT as TEXT, PROGRAM_SCHEMA as BASE_SCHEMA, PROGRAM_NAME as BASE_OBJ `,
99-
`from QSYS2.SQLPACKAGE`,
100-
`where PACKAGE_SCHEMA = '${schema}' ${details.filter ? `and PACKAGE_NAME like '%${details.filter}%'`: ``}`,
101-
`order by PACKAGE_NAME asc`
102-
].join(` `);
103-
break;
104-
105-
case `triggers`:
106-
query = [
107-
`select TRIGGER_NAME as NAME, TRIGGER_TEXT as TEXT, EVENT_OBJECT_SCHEMA as BASE_SCHEMA, EVENT_OBJECT_TABLE as BASE_OBJ `,
108-
`from QSYS2.SYSTRIGGERS`,
109-
`where TRIGGER_SCHEMA = '${schema}' ${details.filter ? `and TRIGGER_NAME like '%${details.filter}%'`: ``}`,
110-
`order by TRIGGER_NAME asc`
111-
].join(` `);
112-
break;
113-
114-
case `types`:
115-
query = [
116-
`select USER_DEFINED_TYPE_NAME as NAME, TYPE_TEXT as TEXT, SYSTEM_TYPE_NAME as SYS_NAME, SYSTEM_TYPE_SCHEMA as SYS_SCHEMA `,
117-
`from QSYS2.SYSTYPES`,
118-
`where USER_DEFINED_TYPE_SCHEMA = '${schema}' ${details.filter ? `and USER_DEFINED_TYPE_NAME like '%${details.filter}%'`: ``}`,
119-
`order by USER_DEFINED_TYPE_NAME asc`
120-
].join(` `);
121-
break;
25+
26+
if (details.filter) {
27+
// This is so we can do a case insensitive filter
28+
details.filter = details.filter.toUpperCase();
12229
}
12330

31+
// If there are multiple types, we build a union. It's important that the ordering of the columns in the selects are consistant:
32+
// OBJ_TYPE, NAME, TEXT, SYS_NAME, SYS_SCHEMA, SPECNAME, BASE_SCHEMA, BASE_OBJ
33+
34+
for (const type of types) {
35+
switch (type) {
36+
case `schemas`:
37+
selects.push([
38+
`select '${type}' as OBJ_TYPE, SCHEMA_NAME as NAME, SCHEMA_TEXT as TEXT, SYSTEM_SCHEMA_NAME as SYS_NAME, '' as SYS_SCHEMA, '' as SPECNAME, '' as BASE_SCHEMA, '' as BASE_OBJ`,
39+
`from QSYS2.SYSSCHEMAS`,
40+
details.filter ? `where UPPER(SCHEMA_NAME) = '${details.filter}' or UPPER(SYSTEM_SCHEMA_NAME) = '${details.filter}'` : ``,
41+
`order by QSYS2.DELIMIT_NAME(SCHEMA_NAME) asc`
42+
].join(` `));
43+
break;
44+
45+
case `tables`:
46+
case `views`:
47+
case `aliases`:
48+
selects.push([
49+
`select '${type}' as OBJ_TYPE, TABLE_NAME as NAME, TABLE_TEXT as TEXT, SYSTEM_TABLE_NAME as SYS_NAME, SYSTEM_TABLE_SCHEMA as SYS_SCHEMA, '' as SPECNAME, BASE_TABLE_SCHEMA as BASE_SCHEMA, BASE_TABLE_NAME as BASE_OBJ`,
50+
`from QSYS2.SYSTABLES`,
51+
`where TABLE_SCHEMA = '${schema}' and TABLE_TYPE in (${typeMap[type].map(item => `'${item}'`).join(`, `)}) ${details.filter ? `and UPPER(TABLE_NAME) like '%${details.filter}%'`: ``}`,
52+
`order by TABLE_NAME asc`
53+
].join(` `));
54+
break;
55+
56+
case `constraints`:
57+
selects.push([
58+
`select '${type}' as OBJ_TYPE, CONSTRAINT_NAME as NAME, CONSTRAINT_TEXT as TEXT, SYSTEM_TABLE_NAME as SYS_NAME, SYSTEM_TABLE_SCHEMA as SYS_SCHEMA, '' as SPECNAME, TABLE_SCHEMA as BASE_SCHEMA, TABLE_NAME as BASE_OBJ`,
59+
`from QSYS2.SYSCST`,
60+
`where CONSTRAINT_SCHEMA = '${schema}' ${details.filter ? `and UPPER(CONSTRAINT_NAME) like '%${details.filter}%'`: ``}`,
61+
`order by CONSTRAINT_NAME asc`
62+
].join(` `));
63+
break;
64+
65+
case `functions`:
66+
selects.push([
67+
`select '${type}' as OBJ_TYPE, ROUTINE_NAME as NAME, coalesce(ROUTINE_TEXT, LONG_COMMENT) as TEXT, '' as SYS_NAME, '' as SYS_SCHEMA, SPECIFIC_NAME as SPECNAME, '' as BASE_SCHEMA, '' as BASE_OBJ`,
68+
`from QSYS2.SYSFUNCS`,
69+
`where ROUTINE_SCHEMA = '${schema}' ${details.filter ? `and UPPER(ROUTINE_NAME) like '%${details.filter}%'`: ``} and FUNCTION_ORIGIN in ('E','U')`,
70+
`order by ROUTINE_NAME asc`
71+
].join(` `));
72+
break;
73+
74+
case `variables`:
75+
selects.push([
76+
`select '${type}' as OBJ_TYPE, VARIABLE_NAME as NAME, VARIABLE_TEXT as TEXT, SYSTEM_VAR_NAME as SYS_NAME, SYSTEM_VAR_SCHEMA as SYS_SCHEMA, '' as SPECNAME, '' as BASE_SCHEMA, '' as BASE_OBJ`,
77+
`from QSYS2.SYSVARIABLES`,
78+
`where VARIABLE_SCHEMA = '${schema}' ${details.filter ? `and UPPER(VARIABLE_NAME) like '%${details.filter}%'`: ``}`,
79+
`order by VARIABLE_NAME asc`
80+
].join(` `));
81+
break;
82+
83+
case `indexes`:
84+
selects.push([
85+
`select '${type}' as OBJ_TYPE, INDEX_NAME as NAME, INDEX_TEXT as TEXT, SYSTEM_INDEX_NAME as SYS_NAME, SYSTEM_INDEX_SCHEMA as SYS_SCHEMA, '' as SPECNAME, TABLE_SCHEMA as BASE_SCHEMA, TABLE_NAME as BASE_OBJ`,
86+
`from QSYS2.SYSINDEXES`,
87+
`where INDEX_SCHEMA = '${schema}' ${details.filter ? `and UPPER(INDEX_NAME) like '%${details.filter}%'`: ``}`,
88+
`order by INDEX_NAME asc`
89+
].join(` `));
90+
break;
91+
92+
case `procedures`:
93+
selects.push([
94+
`select '${type}' as OBJ_TYPE, ROUTINE_NAME as NAME, ROUTINE_TEXT as TEXT, '' as SYS_NAME, '' as SYS_SCHEMA, SPECIFIC_NAME as SPECNAME, '' as BASE_SCHEMA, '' as BASE_OBJ`,
95+
`from QSYS2.SYSPROCS`,
96+
`where ROUTINE_SCHEMA = '${schema}' ${details.filter ? `and UPPER(ROUTINE_NAME) like '%${details.filter}%'`: ``}`,
97+
`order by ROUTINE_NAME asc`
98+
].join(` `));
99+
break;
100+
101+
case `sequences`:
102+
selects.push([
103+
`select '${type}' as OBJ_TYPE, SEQUENCE_NAME as NAME, SEQUENCE_TEXT as TEXT, SYSTEM_SEQ_NAME as SYS_NAME, SYSTEM_SEQ_SCHEMA as SYS_SCHEMA, '' as SPECNAME, '' as BASE_SCHEMA, '' as BASE_OBJ`,
104+
`from QSYS2.SYSSEQUENCES`,
105+
`where SEQUENCE_SCHEMA = '${schema}' ${details.filter ? `and UPPER(SEQUENCE_NAME) like '%${details.filter}%'`: ``}`,
106+
`order by SEQUENCE_NAME asc`
107+
].join(` `));
108+
break;
109+
110+
// case `packages`:
111+
// selects.push([
112+
// `select '${type}' as OBJ_TYPE, PACKAGE_NAME as NAME, PACKAGE_TEXT as TEXT, PROGRAM_SCHEMA as BASE_SCHEMA, PROGRAM_NAME as BASE_OBJ, `,
113+
// ` '' as SYS_SCHEMA, '' as SYS_NAME, '' as SPECNAME`,
114+
// `from QSYS2.SQLPACKAGE`,
115+
// `where PACKAGE_SCHEMA = '${schema}' ${details.filter ? `and PACKAGE_NAME like '%${details.filter}%'`: ``}`,
116+
// `order by PACKAGE_NAME asc`
117+
// ].join(` `));
118+
// break;
119+
120+
case `triggers`:
121+
selects.push([
122+
`select '${type}' as OBJ_TYPE, TRIGGER_NAME as NAME, TRIGGER_TEXT as TEXT, '' as SYS_NAME, '' as SYS_SCHEMA, '' as SPECNAME, EVENT_OBJECT_SCHEMA as BASE_SCHEMA, EVENT_OBJECT_TABLE as BASE_OBJ`,
123+
`from QSYS2.SYSTRIGGERS`,
124+
`where TRIGGER_SCHEMA = '${schema}' ${details.filter ? `and UPPER(TRIGGER_NAME) like '%${details.filter}%'`: ``}`,
125+
`order by TRIGGER_NAME asc`
126+
].join(` `));
127+
break;
128+
129+
case `types`:
130+
selects.push([
131+
`select '${type}' as OBJ_TYPE, USER_DEFINED_TYPE_NAME as NAME, TYPE_TEXT as TEXT, SYSTEM_TYPE_NAME as SYS_NAME, SYSTEM_TYPE_SCHEMA as SYS_SCHEMA, '' as SPECNAME, '' as BASE_SCHEMA, '' as BASE_OBJ`,
132+
`from QSYS2.SYSTYPES`,
133+
`where USER_DEFINED_TYPE_SCHEMA = '${schema}' ${details.filter ? `and UPPER(USER_DEFINED_TYPE_NAME) like '%${details.filter}%'`: ``}`,
134+
`order by USER_DEFINED_TYPE_NAME asc`
135+
].join(` `));
136+
break;
137+
}
138+
}
139+
140+
query = selects.map(s => `select * from (${s})`).join(`\n UNION ALL \n`);
141+
124142
let objects : any[] = await JobManager.runSQL([
125143
query,
126144
`${details.limit ? `limit ${details.limit}` : ``} ${details.offset ? `offset ${details.offset}` : ``}`
127145
].join(` `));
128146

129147
return objects.map(object => ({
130-
type,
148+
type: object.OBJ_TYPE,
131149
schema,
132-
name: object.NAME,
133-
specificName: object.SPECNAME,
134-
text: object.TEXT,
150+
name: object.NAME || undefined,
151+
specificName: object.SPECNAME || undefined,
152+
text: object.TEXT || undefined,
135153
system: {
136-
schema: object.SYS_SCHEMA,
137-
name: object.SYS_NAME,
154+
schema: object.SYS_SCHEMA || undefined,
155+
name: object.SYS_NAME || undefined,
138156
},
139157
basedOn: {
140-
schema: object.BASE_SCHEMA,
141-
name: object.BASE_OBJ
158+
schema: object.BASE_SCHEMA || undefined,
159+
name: object.BASE_OBJ || undefined
142160
}
143161
}));
144162
}

src/language/providers/completionProvider.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ async function getObjectColumns(
133133
let completionItems: CompletionItem[] = [];
134134

135135
if (isUDTF) {
136-
const resultSet = await Callable.getResultColumns(schema, name);
136+
const resultSet = await Callable.getResultColumns(schema, name, true);
137137

138138
if (!resultSet?.length ? true : false) {
139139
completionItemCache.set(databaseObj, []);
@@ -187,7 +187,7 @@ async function getObjectCompletions(
187187
if (!completionItemCache.has(forSchema) || schemaUpdate) {
188188
const promises = Object.entries(sqlTypes).map(async ([_, value]) => {
189189
forSchema = Statement.noQuotes(Statement.delimName(forSchema, true));
190-
const data = await Database.getObjects(forSchema, value.type);
190+
const data = await Database.getObjects(forSchema, [value.type]);
191191
return data.map((table) =>
192192
createCompletionItem(
193193
Statement.prettyName(table.name),
@@ -212,7 +212,7 @@ async function getObjectCompletions(
212212
async function getCompletionItemsForSchema(
213213
schema: string
214214
): Promise<CompletionItem[]> {
215-
const data = await Database.getObjects(schema, "procedures");
215+
const data = await Database.getObjects(schema, ["procedures"]);
216216
return data.map((item) =>
217217
createCompletionItem(
218218
item.name,
@@ -340,7 +340,7 @@ async function getCachedSchemas() {
340340

341341
const allSchemas: BasicSQLObject[] = await Schemas.getObjects(
342342
undefined,
343-
`schemas`
343+
[`schemas`]
344344
);
345345
const completionItems: CompletionItem[] = allSchemas.map((schema) =>
346346
createCompletionItem(

0 commit comments

Comments
 (0)