diff --git a/package.json b/package.json index 7119bf0c..b33a0881 100644 --- a/package.json +++ b/package.json @@ -127,6 +127,106 @@ } } }, + { + "id": "vscode-db2i.schemaBrowser", + "title": "Schema Browser", + "properties": { + "vscode-db2i.schemaBrowser.databaseObjectTypes": { + "type": "object", + "default": { + "Aliases": true, + "Column Masks": true, + "Constraints": true, + "Functions": true, + "Global Variables": true, + "Indexes": true, + "Journal Receivers": true, + "Journals": true, + "Logicals": true, + "Procedures": true, + "Row Permissions": true, + "Sequences": true, + "SQL Packages": true, + "Tables": true, + "Triggers": true, + "Types": true, + "Views": true + }, + "properties": { + "Aliases": { + "type": "boolean", + "default": true + }, + "Column Masks": { + "type": "boolean", + "default": true + }, + "Constraints": { + "type": "boolean", + "default": true + }, + "Functions": { + "type": "boolean", + "default": true + }, + "Global Variables": { + "type": "boolean", + "default": true + }, + "Indexes": { + "type": "boolean", + "default": true + }, + "Journal Receivers": { + "type": "boolean", + "default": true + }, + "Journals": { + "type": "boolean", + "default": true + }, + "Logicals": { + "type": "boolean", + "default": true + }, + "Procedures": { + "type": "boolean", + "default": true + }, + "Row Permissions": { + "type": "boolean", + "default": true + }, + "Sequences": { + "type": "boolean", + "default": true + }, + "SQL Packages": { + "type": "boolean", + "default": true + }, + "Tables": { + "type": "boolean", + "default": true + }, + "Triggers": { + "type": "boolean", + "default": true + }, + "Types": { + "type": "boolean", + "default": true + }, + "Views": { + "type": "boolean", + "default": true + } + }, + "additionalProperties": false, + "description": "Select which database object types should be shown in the Schema Browser" + } + } + }, { "id": "vscode-db2i.resultsets", "title": "Viewing Data", @@ -439,6 +539,12 @@ "category": "Db2 for i", "icon": "$(list-selection)" }, + { + "command": "vscode-db2i.filterDatabaseObjectTypes", + "title": "Filter Database Object Types", + "category": "Db2 for i", + "icon": "$(filter)" + }, { "command": "vscode-db2i.removeSchemaFromSchemaBrowser", "title": "Remove schema from view", @@ -522,11 +628,6 @@ "category": "Db2 for i", "icon": "$(search)" }, - { - "command": "vscode-db2i.viewPermissions", - "title": "View permissions", - "category": "Db2 for i" - }, { "command": "vscode-db2i.runEditorStatement", "title": "Run statement", @@ -876,6 +977,10 @@ "command": "vscode-db2i.importDataContextMenu", "when": "false" }, + { + "command": "vscode-db2i.filterDatabaseObjectTypes", + "when": "never" + }, { "command": "vscode-db2i.setSchemaFilter", "when": "never" @@ -1040,14 +1145,19 @@ ], "view/title": [ { - "command": "vscode-db2i.refreshSchemaBrowser", - "group": "navigation", + "command": "vscode-db2i.manageSchemaBrowserList", + "group": "navigation@0", + "when": "view == schemaBrowser && vscode-db2i:manageSchemaBrowserEnabled" + }, + { + "command": "vscode-db2i.filterDatabaseObjectTypes", + "group": "navigation@1", "when": "view == schemaBrowser" }, { - "command": "vscode-db2i.manageSchemaBrowserList", - "group": "navigation", - "when": "view == schemaBrowser && vscode-db2i:manageSchemaBrowserEnabled" + "command": "vscode-db2i.refreshSchemaBrowser", + "group": "navigation@2", + "when": "view == schemaBrowser" }, { "command": "vscode-db2i.resultset.settings", @@ -1193,7 +1303,7 @@ }, { "command": "vscode-db2i.generateSQL", - "when": "viewItem == table || viewItem == view || viewItem == alias || viewItem == constraint || viewItem == function || viewItem == variable || viewItem == index || viewItem == procedure || viewItem == sequence || viewItem == package || viewItem == trigger || viewItem == type", + "when": "viewItem == table || viewItem == view || viewItem == alias || viewItem == mask || viewItem == constraint || viewItem == function || viewItem == variable || viewItem == index || viewItem == procedure || viewItem == permission || viewItem == sequence || viewItem == trigger || viewItem == type", "group": "db2@2" }, { @@ -1218,7 +1328,7 @@ }, { "command": "vscode-db2i.getAuthorities", - "when": "viewItem == table || viewItem == view || viewItem == alias || viewItem == constraint || viewItem == function || viewItem == variable || viewItem == index || viewItem == procedure || viewItem == sequence || viewItem == package || viewItem == trigger || viewItem == type", + "when": "viewItem == table || viewItem == view || viewItem == alias || viewItem == function || viewItem == variable || viewItem == index || viewItem == procedure || viewItem == receiver || viewItem == journal || viewItem == sequence || viewItem == package || viewItem == trigger || viewItem == type", "group": "db2workWith@4" }, { @@ -1243,7 +1353,7 @@ }, { "command": "vscode-db2i.deleteObject", - "when": "viewItem == table || viewItem == view || viewItem == alias || viewItem == constraint || viewItem == function || viewItem == variable || viewItem == index || viewItem == procedure || viewItem == sequence || viewItem == package || viewItem == trigger || viewItem == type", + "when": "viewItem == table || viewItem == view || viewItem == alias || viewItem == mask || viewItem == constraint || viewItem == function || viewItem == variable || viewItem == index || viewItem == procedure || viewItem == permission || viewItem == sequence || viewItem == package || viewItem == trigger || viewItem == type", "group": "db2data@3" }, { @@ -1251,11 +1361,6 @@ "when": "viewItem == table || viewItem == view || viewItem == index", "group": "db2data@4" }, - { - "command": "vscode-db2i.viewPermissions", - "when": "viewItem == table || viewItem == view || viewItem == alias || viewItem == function || viewItem == variable || viewItem == index || viewItem == procedure || viewItem == sequence || viewItem == package || viewItem == trigger || viewItem == type", - "group": "db2data@5" - }, { "command": "vscode-db2i.advisedIndexes", "when": "viewItem == table || viewItem == schema", diff --git a/src/database/schemas.ts b/src/database/schemas.ts index 40e37d34..6b6f03bc 100644 --- a/src/database/schemas.ts +++ b/src/database/schemas.ts @@ -4,7 +4,7 @@ import { getInstance } from "../base"; import { JobManager } from "../config"; import { ResolvedSqlObject, BasicSQLObject } from "../types"; -export type SQLType = "schemas" | "tables" | "views" | "aliases" | "constraints" | "functions" | "variables" | "indexes" | "procedures" | "sequences" | "packages" | "triggers" | "types" | "logicals"; +export type SQLType = "schemas" | "tables" | "views" | "aliases" | "masks" | "constraints" | "functions" | "variables" | "indexes" | "procedures" | "receivers" | "journals" | "permissions" | "sequences" | "packages" | "triggers" | "types" | "logicals"; export type PageData = { filter?: string, offset?: number, limit?: number, sort?: boolean }; const typeMap = { @@ -14,17 +14,21 @@ const typeMap = { 'logicals': [`L`], }; -export const AllSQLTypes: SQLType[] = ["tables", "views", "aliases", "constraints", "functions", "variables", "indexes", "procedures", "sequences", "packages", "triggers", "types", "logicals"]; +export const AllSQLTypes: SQLType[] = ["tables", "views", "aliases", "masks", "constraints", "functions", "variables", "indexes", "procedures", "receivers", "journals", "permissions", "sequences", "packages", "triggers", "types", "logicals"]; export const InternalTypes: { [t: string]: string } = { "tables": `table`, "views": `view`, "aliases": `alias`, + "masks": `mask`, "constraints": `constraint`, "functions": `function`, "variables": `variable`, "indexes": `index`, "procedures": `procedure`, + "receivers": `receiver`, + "journals": `journal`, + "permissions": `permission`, "sequences": `sequence`, "packages": `package`, "triggers": `trigger`, @@ -245,7 +249,7 @@ export default class Schemas { let filter: PartStatementInfo; // If there are multiple types, we build a union. It's important that the ordering of the columns in the selects are consistant: - // OBJ_TYPE, TABLE_TYPE, NAME, TEXT, SYS_NAME, SYS_SCHEMA, SPECNAME, BASE_SCHEMA, BASE_OBJ + // OBJ_TYPE, TABLE_TYPE, CONSTRAINT_TYPE, NAME, TEXT, SYS_NAME, SYS_SCHEMA, SPECNAME, BASE_SCHEMA, BASE_OBJ for (const type of types) { switch (type) { @@ -253,7 +257,7 @@ export default class Schemas { selects.push( [ ``, - `SELECT '${type}' as OBJ_TYPE, '' as TABLE_TYPE, OBJLONGNAME AS NAME, '' as TEXT, OBJNAME AS SYS_NAME, '' as SYS_SCHEMA, '' as SPECNAME, '' as BASE_SCHEMA, '' as BASE_OBJ`, + `SELECT '${type}' as OBJ_TYPE, '' as TABLE_TYPE, '' as CONSTRAINT_TYPE, OBJLONGNAME AS NAME, '' as TEXT, OBJNAME AS SYS_NAME, '' as SYS_SCHEMA, '' as SPECNAME, '' as BASE_SCHEMA, '' as BASE_OBJ`, `FROM TABLE(QSYS2.OBJECT_STATISTICS('*ALLSIMPLE', 'LIB')) Z`, details.filter ? `where UPPER(OBJLONGNAME) = ? or UPPER(OBJNAME) = ?` @@ -273,7 +277,7 @@ export default class Schemas { filter = getFilterClause(`TABLE_NAME`, details.filter); selects.push( [ - `select '${type}' as OBJ_TYPE, TABLE_TYPE as TABLE_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`, + `select '${type}' as OBJ_TYPE, TABLE_TYPE as TABLE_TYPE, '' as CONSTRAINT_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`, `from QSYS2.SYSTABLES`, `where TABLE_SCHEMA = ? and TABLE_TYPE in (${typeMap[type] .map((item) => `'${item}'`) @@ -284,11 +288,24 @@ export default class Schemas { parameters.push(schema, ...filter.parameters); break; + case "masks": + filter = getFilterClause(`NAME`, details.filter); + selects.push( + [ + `select '${type}' as OBJ_TYPE, '' as TABLE_TYPE, '' as CONSTRAINT_TYPE, NAME as NAME, COALESCE(LABEL, '') as TEXT, '' as SYS_NAME, '' as SYS_SCHEMA, '' as SPECNAME, TABLE_SCHEMA as BASE_SCHEMA, TABLE_NAME as BASE_OBJ`, + `from QSYS2.SYSCONTROLS`, + `where SCHEMA = ? AND CONTROL_TYPE = 'M' ${filter.clause}`, + ].join(` `) + ); + + parameters.push(schema, ...filter.parameters); + break; + case `constraints`: filter = getFilterClause(`CONSTRAINT_NAME`, details.filter); selects.push( [ - `select '${type}' as OBJ_TYPE, '' as TABLE_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`, + `select '${type}' as OBJ_TYPE, '' as TABLE_TYPE, CONSTRAINT_TYPE as CONSTRAINT_TYPE, CONSTRAINT_NAME as NAME, CONSTRAINT_TEXT as TEXT, '' as SYS_NAME, '' as SYS_SCHEMA, '' as SPECNAME, TABLE_SCHEMA as BASE_SCHEMA, TABLE_NAME as BASE_OBJ`, `from QSYS2.SYSCST`, `where CONSTRAINT_SCHEMA = ? ${filter.clause}`, ].join(` `) @@ -301,7 +318,7 @@ export default class Schemas { filter = getFilterClause(`ROUTINE_NAME`, details.filter); selects.push( [ - `select '${type}' as OBJ_TYPE, '' as TABLE_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`, + `select '${type}' as OBJ_TYPE, '' as TABLE_TYPE, '' as CONSTRAINT_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`, `from QSYS2.SYSFUNCS`, `where ROUTINE_SCHEMA = ? ${filter.clause} and FUNCTION_ORIGIN in ('E','U')`, ].join(` `) @@ -314,7 +331,7 @@ export default class Schemas { filter = getFilterClause(`VARIABLE_NAME`, details.filter); selects.push( [ - `select '${type}' as OBJ_TYPE, '' as TABLE_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`, + `select '${type}' as OBJ_TYPE, '' as TABLE_TYPE, '' as CONSTRAINT_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`, `from QSYS2.SYSVARIABLES`, `where VARIABLE_SCHEMA = ? ${filter.clause}`, ].join(` `) @@ -327,7 +344,7 @@ export default class Schemas { filter = getFilterClause(`INDEX_NAME`, details.filter); selects.push( [ - `select '${type}' as OBJ_TYPE, '' as TABLE_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`, + `select '${type}' as OBJ_TYPE, '' as TABLE_TYPE, '' as CONSTRAINT_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`, `from QSYS2.SYSINDEXES`, `where INDEX_SCHEMA = ? ${filter.clause}`, ].join(` `) @@ -340,7 +357,7 @@ export default class Schemas { filter = getFilterClause(`ROUTINE_NAME`, details.filter); selects.push( [ - `select '${type}' as OBJ_TYPE, '' as TABLE_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`, + `select '${type}' as OBJ_TYPE, '' as TABLE_TYPE, '' as CONSTRAINT_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`, `from QSYS2.SYSPROCS`, `where ROUTINE_SCHEMA = ? ${filter.clause}`, ].join(` `) @@ -349,11 +366,50 @@ export default class Schemas { parameters.push(schema, ...filter.parameters); break; + case "receivers": + filter = getFilterClause(`JOURNAL_RECEIVER_NAME`, details.filter); + selects.push( + [ + `select '${type}' as OBJ_TYPE, '' as TABLE_TYPE, '' as CONSTRAINT_TYPE, JOURNAL_RECEIVER_NAME as NAME, DESCRIPTIVE_TEXT as TEXT, '' as SYS_NAME, '' as SYS_SCHEMA, '' as SPECNAME, JOURNAL_LIBRARY as BASE_SCHEMA, JOURNAL_NAME as BASE_OBJ`, + `from QSYS2.JOURNAL_RECEIVER_INFO`, + `where JOURNAL_RECEIVER_LIBRARY = ? ${filter.clause}`, + ].join(` `) + ); + + parameters.push(schema, ...filter.parameters); + break; + + case "journals": + filter = getFilterClause(`JOURNAL_NAME`, details.filter); + selects.push( + [ + `select '${type}' as OBJ_TYPE, '' as TABLE_TYPE, '' as CONSTRAINT_TYPE, JOURNAL_NAME as NAME, JOURNAL_TEXT as TEXT, '' as SYS_NAME, '' as SYS_SCHEMA, '' as SPECNAME, JOURNAL_LIBRARY as BASE_SCHEMA, ATTACHED_JOURNAL_RECEIVER_NAME as BASE_OBJ`, + `from QSYS2.JOURNAL_INFO`, + `where JOURNAL_LIBRARY = ? ${filter.clause}`, + ].join(` `) + ); + + parameters.push(schema, ...filter.parameters); + break; + + case "permissions": + filter = getFilterClause(`NAME`, details.filter); + selects.push( + [ + `select '${type}' as OBJ_TYPE, '' as TABLE_TYPE, '' as CONSTRAINT_TYPE, NAME as NAME, COALESCE(LABEL, '') as TEXT, '' as SYS_NAME, '' as SYS_SCHEMA, '' as SPECNAME, TABLE_SCHEMA as BASE_SCHEMA, TABLE_NAME as BASE_OBJ`, + `from QSYS2.SYSCONTROLS`, + `where SCHEMA = ? and CONTROL_TYPE = 'R' ${filter.clause}`, + ].join(` `) + ); + + parameters.push(schema, ...filter.parameters); + break; + case `sequences`: filter = getFilterClause(`SEQUENCE_NAME`, details.filter); selects.push( [ - `select '${type}' as OBJ_TYPE, '' as TABLE_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`, + `select '${type}' as OBJ_TYPE, '' as TABLE_TYPE, '' as CONSTRAINT_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`, `from QSYS2.SYSSEQUENCES`, `where SEQUENCE_SCHEMA = ? ${filter.clause}`, ].join(` `) @@ -362,20 +418,24 @@ export default class Schemas { parameters.push(schema, ...filter.parameters); break; - // case `packages`: - // selects.push([ - // `select '${type}' as OBJ_TYPE, '' as TABLE_TYPE, PACKAGE_NAME as NAME, PACKAGE_TEXT as TEXT, PROGRAM_SCHEMA as BASE_SCHEMA, PROGRAM_NAME as BASE_OBJ, `, - // ` '' as SYS_SCHEMA, '' as SYS_NAME, '' as SPECNAME`, - // `from QSYS2.SQLPACKAGE`, - // `where PACKAGE_SCHEMA = '${schema}' ${details.filter ? `and PACKAGE_NAME like '%${filter.clause}%'`: ``}`, - // ].join(` `)); - // break; + case "packages": + filter = getFilterClause(`PACKAGE_NAME`, details.filter); + selects.push( + [ + `select '${type}' as OBJ_TYPE, '' as TABLE_TYPE, '' as CONSTRAINT_TYPE, PACKAGE_NAME as NAME, PACKAGE_TEXT as TEXT, SYSTEM_PACKAGE_NAME as SYS_NAME, SYSTEM_PACKAGE_SCHEMA as SYS_SCHEMA, '' as SPECNAME, PACKAGE_SCHEMA as BASE_SCHEMA, PROGRAM_NAME as BASE_OBJ`, + `from QSYS2.SYSPACKAGE`, + `where PACKAGE_SCHEMA = ? ${filter.clause}`, + ].join(` `) + ); + + parameters.push(schema, ...filter.parameters); + break; case `triggers`: filter = getFilterClause(`TRIGGER_NAME`, details.filter); selects.push( [ - `select '${type}' as OBJ_TYPE, '' as TABLE_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`, + `select '${type}' as OBJ_TYPE, '' as TABLE_TYPE, '' as CONSTRAINT_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`, `from QSYS2.SYSTRIGGERS`, `where TRIGGER_SCHEMA = ? ${filter.clause}`, ].join(` `) @@ -388,7 +448,7 @@ export default class Schemas { filter = getFilterClause(`USER_DEFINED_TYPE_NAME`, details.filter); selects.push( [ - `select '${type}' as OBJ_TYPE, '' as TABLE_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`, + `select '${type}' as OBJ_TYPE, '' as TABLE_TYPE, '' as CONSTRAINT_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`, `from QSYS2.SYSTYPES`, `where USER_DEFINED_TYPE_SCHEMA = ? ${filter.clause}`, ].join(` `) @@ -423,6 +483,7 @@ export default class Schemas { return objects.map((object) => ({ type: object.OBJ_TYPE, tableType: object.TABLE_TYPE, + constraintType: object.CONSTRAINT_TYPE, schema, name: object.NAME || object.SYS_NAME || undefined, specificName: object.SPECNAME || undefined, @@ -491,10 +552,13 @@ export default class Schemas { static async deleteObject( schema: string, name: string, - type: string + type: string, + table?: string, + constraintType?: string ): Promise { - const query = `DROP ${(this.isRoutineType(type) ? "SPECIFIC " : "") + type - } IF EXISTS ${schema}.${name}`; + const query = type === 'constraint' ? + `ALTER TABLE ${schema}.${table} DROP ${constraintType === 'PRIMARY KEY' ? constraintType : `${constraintType} ${schema}.${name}`}` : + `DROP ${(this.isRoutineType(type) ? "SPECIFIC " : "") + type} IF EXISTS ${schema}.${name}`; await getInstance().getContent().runSQL(query); } diff --git a/src/types.ts b/src/types.ts index 14ea70b5..9eae89f1 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,67 +1,68 @@ -// https://www.ibm.com/docs/en/i/7.4?topic=views-syscolumns2 -export interface TableColumn { - TABLE_SCHEMA: string, - TABLE_NAME: string, - COLUMN_NAME: string, - SYSTEM_COLUMN_NAME: string, - CONSTRAINT_NAME?: string, - DATA_TYPE: string, - CHARACTER_MAXIMUM_LENGTH?: number, - NUMERIC_SCALE?: number, - NUMERIC_PRECISION?: number, - IS_NULLABLE: "Y" | "N", - HAS_DEFAULT: "Y" | "N", - COLUMN_DEFAULT?: string, - COLUMN_TEXT: string, - IS_IDENTITY: "YES" | "NO", -} - -// https://www.ibm.com/docs/en/i/7.4?topic=views-sysparms -export interface SQLParm { - SPECIFIC_SCHEMA: string, - SPECIFIC_NAME: string, - PARAMETER_NAME: string, - PARAMETER_MODE: "IN" | "OUT" | "INOUT", - DATA_TYPE: string, - CHARACTER_MAXIMUM_LENGTH?: number, - NUMERIC_SCALE?: number, - NUMERIC_PRECISION?: number, - IS_NULLABLE: "YES" | "NO", - DEFAULT?: string, - LONG_COMMENT?: string, - ORDINAL_POSITION: number, - ROW_TYPE: "P" | "R", -} - -export interface ResolvedSqlObject { - schema: string; - name: string; - sqlType: string; -} - -export interface BasicSQLObject { - type: string; - tableType: string; - schema: string; - name: string; - specificName: string; - text: string; - system: { - schema: string; - name: string; - } - basedOn: { - schema: string; - name: string; - } -} - -export interface CPYFOptions { - toLib: string; - toFile: string; - fromMbr: string; - toMbr: string; - mbrOpt: string; - crtFile: string; - outFmt: string +// https://www.ibm.com/docs/en/i/7.4?topic=views-syscolumns2 +export interface TableColumn { + TABLE_SCHEMA: string, + TABLE_NAME: string, + COLUMN_NAME: string, + SYSTEM_COLUMN_NAME: string, + CONSTRAINT_NAME?: string, + DATA_TYPE: string, + CHARACTER_MAXIMUM_LENGTH?: number, + NUMERIC_SCALE?: number, + NUMERIC_PRECISION?: number, + IS_NULLABLE: "Y" | "N", + HAS_DEFAULT: "Y" | "N", + COLUMN_DEFAULT?: string, + COLUMN_TEXT: string, + IS_IDENTITY: "YES" | "NO", +} + +// https://www.ibm.com/docs/en/i/7.4?topic=views-sysparms +export interface SQLParm { + SPECIFIC_SCHEMA: string, + SPECIFIC_NAME: string, + PARAMETER_NAME: string, + PARAMETER_MODE: "IN" | "OUT" | "INOUT", + DATA_TYPE: string, + CHARACTER_MAXIMUM_LENGTH?: number, + NUMERIC_SCALE?: number, + NUMERIC_PRECISION?: number, + IS_NULLABLE: "YES" | "NO", + DEFAULT?: string, + LONG_COMMENT?: string, + ORDINAL_POSITION: number, + ROW_TYPE: "P" | "R", +} + +export interface ResolvedSqlObject { + schema: string; + name: string; + sqlType: string; +} + +export interface BasicSQLObject { + type: string; + tableType: string; + constraintType: string; + schema: string; + name: string; + specificName: string; + text: string; + system: { + schema: string; + name: string; + } + basedOn: { + schema: string; + name: string; + } +} + +export interface CPYFOptions { + toLib: string; + toFile: string; + fromMbr: string; + toMbr: string; + mbrOpt: string; + crtFile: string; + outFmt: string } \ No newline at end of file diff --git a/src/views/results/index.ts b/src/views/results/index.ts index bb049afb..fa9a0427 100644 --- a/src/views/results/index.ts +++ b/src/views/results/index.ts @@ -379,7 +379,7 @@ async function runHandler(options?: StatementInfo) { } const uiId = registerRunStatement(statementDetail); - const eol = editor.document.eol === vscode.EndOfLine.CRLF ? `\r\n` : `\n`; + const eol = editor?.document.eol === vscode.EndOfLine.CRLF ? `\r\n` : `\n`; const basicSelect = statementDetail.content.split(eol).filter(line => !line.trimStart().startsWith(`--`)).join(eol); chosenView.setScrolling({ // Never errors diff --git a/src/views/schemaBrowser/contributes.json b/src/views/schemaBrowser/contributes.json index 5e5ee70b..fdcccfe5 100644 --- a/src/views/schemaBrowser/contributes.json +++ b/src/views/schemaBrowser/contributes.json @@ -23,6 +23,12 @@ "category": "Db2 for i", "icon": "$(list-selection)" }, + { + "command": "vscode-db2i.filterDatabaseObjectTypes", + "title": "Filter Database Object Types", + "category": "Db2 for i", + "icon": "$(filter)" + }, { "command": "vscode-db2i.removeSchemaFromSchemaBrowser", "title": "Remove schema from view", @@ -105,15 +111,14 @@ "title": "Set filter", "category": "Db2 for i", "icon": "$(search)" - }, - { - "command": "vscode-db2i.viewPermissions", - "title": "View permissions", - "category": "Db2 for i" } ], "menus": { "commandPalette": [ + { + "command": "vscode-db2i.filterDatabaseObjectTypes", + "when": "never" + }, { "command": "vscode-db2i.setSchemaFilter", "when": "never" @@ -173,14 +178,19 @@ ], "view/title": [ { - "command": "vscode-db2i.refreshSchemaBrowser", - "group": "navigation", + "command": "vscode-db2i.manageSchemaBrowserList", + "group": "navigation@0", + "when": "view == schemaBrowser && vscode-db2i:manageSchemaBrowserEnabled" + }, + { + "command": "vscode-db2i.filterDatabaseObjectTypes", + "group": "navigation@1", "when": "view == schemaBrowser" }, { - "command": "vscode-db2i.manageSchemaBrowserList", - "group": "navigation", - "when": "view == schemaBrowser && vscode-db2i:manageSchemaBrowserEnabled" + "command": "vscode-db2i.refreshSchemaBrowser", + "group": "navigation@2", + "when": "view == schemaBrowser" } ], "view/item/context": [ @@ -196,7 +206,7 @@ }, { "command": "vscode-db2i.generateSQL", - "when": "viewItem == table || viewItem == view || viewItem == alias || viewItem == constraint || viewItem == function || viewItem == variable || viewItem == index || viewItem == procedure || viewItem == sequence || viewItem == package || viewItem == trigger || viewItem == type", + "when": "viewItem == table || viewItem == view || viewItem == alias || viewItem == mask || viewItem == constraint || viewItem == function || viewItem == variable || viewItem == index || viewItem == procedure || viewItem == permission || viewItem == sequence || viewItem == trigger || viewItem == type", "group": "db2@2" }, { @@ -221,7 +231,7 @@ }, { "command": "vscode-db2i.getAuthorities", - "when": "viewItem == table || viewItem == view || viewItem == alias || viewItem == constraint || viewItem == function || viewItem == variable || viewItem == index || viewItem == procedure || viewItem == sequence || viewItem == package || viewItem == trigger || viewItem == type", + "when": "viewItem == table || viewItem == view || viewItem == alias || viewItem == function || viewItem == variable || viewItem == index || viewItem == procedure || viewItem == receiver || viewItem == journal || viewItem == sequence || viewItem == package || viewItem == trigger || viewItem == type", "group": "db2workWith@4" }, { @@ -246,7 +256,7 @@ }, { "command": "vscode-db2i.deleteObject", - "when": "viewItem == table || viewItem == view || viewItem == alias || viewItem == constraint || viewItem == function || viewItem == variable || viewItem == index || viewItem == procedure || viewItem == sequence || viewItem == package || viewItem == trigger || viewItem == type", + "when": "viewItem == table || viewItem == view || viewItem == alias || viewItem == mask || viewItem == constraint || viewItem == function || viewItem == variable || viewItem == index || viewItem == procedure || viewItem == permission || viewItem == sequence || viewItem == package || viewItem == trigger || viewItem == type", "group": "db2data@3" }, { @@ -254,11 +264,6 @@ "when": "viewItem == table || viewItem == view || viewItem == index", "group": "db2data@4" }, - { - "command": "vscode-db2i.viewPermissions", - "when": "viewItem == table || viewItem == view || viewItem == alias || viewItem == function || viewItem == variable || viewItem == index || viewItem == procedure || viewItem == sequence || viewItem == package || viewItem == trigger || viewItem == type", - "group": "db2data@5" - }, { "command": "vscode-db2i.advisedIndexes", "when": "viewItem == table || viewItem == schema", @@ -270,6 +275,108 @@ "group": "db2idxAdv@2" } ] - } + }, + "configuration": [ + { + "id": "vscode-db2i.schemaBrowser", + "title": "Schema Browser", + "properties": { + "vscode-db2i.schemaBrowser.databaseObjectTypes": { + "type": "object", + "default": { + "Aliases": true, + "Column Masks": true, + "Constraints": true, + "Functions": true, + "Global Variables": true, + "Indexes": true, + "Journal Receivers": true, + "Journals": true, + "Logicals": true, + "Procedures": true, + "Row Permissions": true, + "Sequences": true, + "SQL Packages": true, + "Tables": true, + "Triggers": true, + "Types": true, + "Views": true + }, + "properties": { + "Aliases": { + "type": "boolean", + "default": true + }, + "Column Masks": { + "type": "boolean", + "default": true + }, + "Constraints": { + "type": "boolean", + "default": true + }, + "Functions": { + "type": "boolean", + "default": true + }, + "Global Variables": { + "type": "boolean", + "default": true + }, + "Indexes": { + "type": "boolean", + "default": true + }, + "Journal Receivers": { + "type": "boolean", + "default": true + }, + "Journals": { + "type": "boolean", + "default": true + }, + "Logicals": { + "type": "boolean", + "default": true + }, + "Procedures": { + "type": "boolean", + "default": true + }, + "Row Permissions": { + "type": "boolean", + "default": true + }, + "Sequences": { + "type": "boolean", + "default": true + }, + "SQL Packages": { + "type": "boolean", + "default": true + }, + "Tables": { + "type": "boolean", + "default": true + }, + "Triggers": { + "type": "boolean", + "default": true + }, + "Types": { + "type": "boolean", + "default": true + }, + "Views": { + "type": "boolean", + "default": true + } + }, + "additionalProperties": false, + "description": "Select which database object types should be shown in the Schema Browser" + } + } + } + ] } } \ No newline at end of file diff --git a/src/views/schemaBrowser/index.ts b/src/views/schemaBrowser/index.ts index 89d974d3..f6ed6e02 100644 --- a/src/views/schemaBrowser/index.ts +++ b/src/views/schemaBrowser/index.ts @@ -1,5 +1,5 @@ -import { ThemeIcon, TreeItem } from "vscode" +import { ThemeIcon, TreeItem, workspace, window } from "vscode" import * as vscode from "vscode" import Schemas, { AllSQLTypes, InternalTypes, SQL_ESCAPE_CHAR, SQLType } from "../../database/schemas"; import Table from "../../database/table"; @@ -25,7 +25,14 @@ const itemIcons = { "trigger": `play`, "variable": `symbol-value`, "index": `list-tree`, - "logical": `symbol-interface` + "logical": `symbol-interface`, + "mask": `map-vertical`, + "constraint": `lock`, + "receiver": `inbox`, + "journal": `note`, + "permission": `key`, + "sequence": `file-binary`, + "package": `package` } export default class schemaBrowser { @@ -99,6 +106,30 @@ export default class schemaBrowser { } }), + vscode.commands.registerCommand(`vscode-db2i.filterDatabaseObjectTypes`, async () => { + const currentTypes = Configuration.get(`schemaBrowser.databaseObjectTypes`); + if (currentTypes) { + const quickPickItems = Object.keys(currentTypes).map((key) => { + return { + label: key, picked: currentTypes[key] + } + }); + const selectedTypes = await window.showQuickPick(quickPickItems, { + title: `Select which database object types should be shown`, + canPickMany: true + }); + if (selectedTypes) { + const unselectedTypes: Record = {}; + for (const key of Object.keys(currentTypes)) { + if (!selectedTypes.find(type => type.label === key)) { + unselectedTypes[key] = false; + } + } + Configuration.set(`schemaBrowser.databaseObjectTypes`, unselectedTypes); + } + } + }), + vscode.commands.registerCommand(`vscode-db2i.removeSchemaFromSchemaBrowser`, async (node: SchemaItem) => { if (node) { //Running from right click @@ -151,22 +182,6 @@ export default class schemaBrowser { } }), - vscode.commands.registerCommand(`vscode-db2i.viewPermissions`, async (object: SQLObject) => { - if (object) { - const content = `SELECT AUTHORIZATION_NAME as USER_NAME, OBJECT_AUTHORITY, - OWNER, OBJECT_OPERATIONAL, OBJECT_MANAGEMENT, OBJECT_EXISTENCE, OBJECT_ALTER, OBJECT_REFERENCE, - DATA_READ, DATA_ADD, DATA_UPDATE, DATA_DELETE, DATA_EXECUTE FROM QSYS2.OBJECT_PRIVILEGES - WHERE OBJECT_SCHEMA='${object.schema}' AND OBJECT_NAME='${object.name}' AND - SQL_OBJECT_TYPE='${object.type.toUpperCase()}'`; - - vscode.commands.executeCommand(`vscode-db2i.runEditorStatement`, { - content, - qualifier: `statement`, - open: false, - }); - } - }), - vscode.commands.registerCommand(`vscode-db2i.getMTIs`, async (object: SQLObject | SchemaItem) => { if (object) { const content = getMTIStatement(object.schema, (`name` in object ? object.name : undefined)); @@ -252,7 +267,7 @@ export default class schemaBrowser { result = await vscode.window.showWarningMessage(`Are you sure you want to clear all of the advised index rows from the Index Advisor for ${object.schema}${isObject ? `${object.name}` : ''}?`, { modal: true, - }, 'No', 'Yes'); + }, 'Yes', 'No'); if (result === 'Yes') { try { @@ -268,7 +283,7 @@ export default class schemaBrowser { if (object) { const result = await vscode.window.showWarningMessage(`Are you sure you want to delete ${object.name}?`, { modal: true, - }, 'No', 'Yes'); + }, 'Yes', 'No'); if (result === 'Yes') { try { @@ -276,7 +291,7 @@ export default class schemaBrowser { location: vscode.ProgressLocation.Notification, title: `Deleting ${object.name}...` }, async () => { - await Schemas.deleteObject(object.schema, object.uniqueName(), object.type); + await Schemas.deleteObject(object.schema, object.uniqueName(), object.type, object.basedOn?.name, object.constraintType); }); vscode.window.showInformationMessage(`${object.name} deleted`); @@ -322,7 +337,7 @@ export default class schemaBrowser { if (object) { const result = await vscode.window.showWarningMessage(`Are you sure you want to clear ${object.name}?`, { modal: true, - }, 'No', 'Yes'); + }, 'Yes', 'No'); if (result === 'Yes') { try { @@ -411,7 +426,7 @@ export default class schemaBrowser { vscode.commands.registerCommand(`vscode-db2i.importData`, async () => { vscode.window.withProgress({ location: vscode.ProgressLocation.Window, title: `Generating SQL` }, async (arg?: any) => { - try { + try { const uri = await this.pickFile(); if (!uri) { return; } const data = await this.readFile(uri); @@ -421,6 +436,12 @@ export default class schemaBrowser { vscode.window.showErrorMessage(e.message); } }); + }), + + workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('vscode-db2i.schemaBrowser.databaseObjectTypes')) { + this.refresh(); + } }) ) @@ -447,14 +468,14 @@ export default class schemaBrowser { async generateInsert(uri: vscode.Uri, data: string) { let ext: string = (uri.fsPath.split('.').pop() || '').toLowerCase(); if (ext != `csv` && ext != `json`) { - ext = await vscode.window.showQuickPick(['csv','json'], { placeHolder: 'What format is this file?' }); + ext = await vscode.window.showQuickPick(['csv', 'json'], { placeHolder: 'What format is this file?' }); if (!ext) { return; } } let rows: any[] = []; let hasHeaders = true; if (ext === `csv`) { - hasHeaders = (await vscode.window.showQuickPick(['Yes','No'], { placeHolder: 'Does the file have headers?' })) === `Yes` ? true : false; + hasHeaders = (await vscode.window.showQuickPick(['Yes', 'No'], { placeHolder: 'Does the file have headers?' })) === `Yes` ? true : false; rows = parse(data, { columns: hasHeaders, cast: true @@ -465,14 +486,14 @@ export default class schemaBrowser { throw new Error('Unsupported JSON format: expected an array of objects.'); } } - - if (!rows.length) { - vscode.window.showWarningMessage('No rows found.'); + + if (!rows.length) { + vscode.window.showWarningMessage('No rows found.'); return; } let content: string = ``; - if(hasHeaders) { + if (hasHeaders) { // Get headers using the first row of data const colNames = Object.keys(rows[0]); const cols = colNames.map(c => c.includes(` `) ? `"${c}"` : c).join(', '); @@ -483,7 +504,7 @@ export default class schemaBrowser { for (let i = 0; i < rows.length; i++) { const row = rows[i]; let allValues = []; - for(const col of colNames) { + for (const col of colNames) { const val = row[col]; if (typeof val === `string`) { allValues.push(`'${val.replace(`'`, `''`)}'`); @@ -492,7 +513,7 @@ export default class schemaBrowser { } } allRowValues.push(` (${allValues.join(', ')})`); - } + } content += allRowValues.join(`,\n`); } else { // Generate the INSERT statement @@ -501,7 +522,7 @@ export default class schemaBrowser { for (let i = 0; i < rows.length; i++) { const row = rows[i]; let allValues = []; - for(let j = 0; j < row.length; j++) { + for (let j = 0; j < row.length; j++) { const val = row[j]; if (typeof val === `string`) { allValues.push(`'${val}'`); @@ -510,12 +531,12 @@ export default class schemaBrowser { } } allRowValues.push(` (${allValues.join(', ')})`); - } + } content += allRowValues.join(`,\n`); } content += `;`; - + // Open the generated SQL in a new file const textDoc = await vscode.workspace.openTextDocument({ language: `sql`, content }); await vscode.window.showTextDocument(textDoc); @@ -690,10 +711,15 @@ class SQLObject extends vscode.TreeItem { specificName: string; type: string; tableType: string; + constraintType: string; system: { schema: string; name: string; } + basedOn: { + schema: string; + name: string; + } constructor(item: BasicSQLObject) { const type = InternalTypes[item.type]; @@ -705,8 +731,10 @@ class SQLObject extends vscode.TreeItem { this.name = item.name; this.specificName = item.specificName; // Only applies to routines this.system = item.system; + this.basedOn = item.basedOn; this.type = type; this.tableType = item.tableType; + this.constraintType = item.constraintType; this.description = item.text; // For functions and procedures, set a tooltip that includes the specific name if (Schemas.isRoutineType(this.type)) { @@ -751,25 +779,26 @@ const getSchemaItems = (schema) => { const items = [ //new SchemaItem(`All Database Objects`, `all`, schema), new SchemaItem(`Aliases`, `aliases`, schema, `symbol-reference`), - //new SchemaItem(`Column Masks`, `masks`, schema), - //new SchemaItem(`Constraints`, `constraints`, schema), - new SchemaItem(`Logicals`, `logicals`, schema, `telescope`), + new SchemaItem(`Column Masks`, `masks`, schema, `layout-centered`), + new SchemaItem(`Constraints`, `constraints`, schema, `group-by-ref-type`), new SchemaItem(`Functions`, `functions`, schema, `symbol-function`), new SchemaItem(`Global Variables`, `variables`, schema, `symbol-variable`), new SchemaItem(`Indexes`, `indexes`, schema, `tag`), - //new SchemaItem(`Journal Receivers`, `receivers`, schema), - //new SchemaItem(`Journals`, `journals`, schema), + new SchemaItem(`Journal Receivers`, `receivers`, schema, `briefcase`), + new SchemaItem(`Journals`, `journals`, schema, `book`), + new SchemaItem(`Logicals`, `logicals`, schema, `telescope`), new SchemaItem(`Procedures`, `procedures`, schema, `gear`), - //new SchemaItem(`Row Permissions`, `permissions`, schema), - //new SchemaItem(`Sequences`, `sequences`, schema), - //new SchemaItem(`SQL Packages`, `packages`, schema), + new SchemaItem(`Row Permissions`, `permissions`, schema, `gist-secret`), + new SchemaItem(`Sequences`, `sequences`, schema, `surround-with`), + new SchemaItem(`SQL Packages`, `packages`, schema, `archive`), new SchemaItem(`Tables`, `tables`, schema, `menu`), new SchemaItem(`Triggers`, `triggers`, schema, `symbol-event`), new SchemaItem(`Types`, `types`, schema, `symbol-type-parameter`), new SchemaItem(`Views`, `views`, schema, `symbol-interface`) ]; - return items; + const databaseObjectTypes = Configuration.get(`schemaBrowser.databaseObjectTypes`); + return databaseObjectTypes ? items.filter(item => databaseObjectTypes[item.label.toString()]) : items; } const moreButton = (schema, type) => { diff --git a/src/views/schemaBrowser/statements.ts b/src/views/schemaBrowser/statements.ts index e7dbc59e..afb365f3 100644 --- a/src/views/schemaBrowser/statements.ts +++ b/src/views/schemaBrowser/statements.ts @@ -263,6 +263,10 @@ export function getAuthoritiesStatement(schema: string, table: string, objectTyp `; if (objectType === 'TABLE' && tableType != 'T') { sql += ` and object_type = '*FILE'`; + } else if (objectType === 'RECEIVER') { + sql += ` and object_type = '*JRNRCV'`; + } else if (objectType === 'JOURNAL') { + sql += ` and object_type = '*JRN'`; } else { sql += ` and sql_object_type = '${objectType}'`; }