diff --git a/package-lock.json b/package-lock.json index cc2e74b..ab9355b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,9 +21,11 @@ "devDependencies": { "@babel/preset-env": "^7.14.2", "@intersystems-community/intersystems-servermanager": "^3.8.0", + "@types/lodash": "^4.17.15", "@types/node": "^14.17.0", "@types/vscode": "^1.93.0", "@vscode/vsce": "^2.19.0", + "lodash": "^4.17.21", "rimraf": "^3.0.2", "ts-loader": "^9.2.1", "typescript": "^4.9.5", @@ -1815,6 +1817,13 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "14.18.52", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.52.tgz", diff --git a/package.json b/package.json index ab6629b..c4411e5 100644 --- a/package.json +++ b/package.json @@ -68,9 +68,11 @@ "devDependencies": { "@babel/preset-env": "^7.14.2", "@intersystems-community/intersystems-servermanager": "^3.8.0", + "@types/lodash": "^4.17.15", "@types/node": "^14.17.0", "@types/vscode": "^1.93.0", "@vscode/vsce": "^2.19.0", + "lodash": "^4.17.21", "rimraf": "^3.0.2", "ts-loader": "^9.2.1", "typescript": "^4.9.5", diff --git a/src/ls/driver.ts b/src/ls/driver.ts index ba4d8be..fa8237d 100644 --- a/src/ls/driver.ts +++ b/src/ls/driver.ts @@ -4,6 +4,7 @@ import { IConnectionDriver, MConnectionExplorer, NSDatabase, ContextValue, Arg0 import { v4 as generateId } from 'uuid'; import IRISdb, { IRISDirect, IQueries } from './irisdb'; import keywordsCompletion from './keywords'; +import zipObject from 'lodash/zipObject'; const toBool = (v: any) => v && (v.toString() === '1' || v.toString().toLowerCase() === 'true' || v.toString().toLowerCase() === 'yes'); @@ -55,21 +56,48 @@ export default class IRISDriver extends AbstractDriver im return queries.split(/;\s*(\n|$)/gm).filter(query => query.trim().length); } + // Handle duplicate column names by appending counter + private getColumnNames(columns: { name: string, type: string }[]): string[] { + return columns.reduce((names, { name }) => { + const count = names.filter((n) => n === name).length; + return names.concat(count > 0 ? `${name} (${count})` : name); + }, []); + } + + // Modify to take account of deduplicated column names + private mapRows(rows: any[], columns: string[]): any[] { + return rows.map((r) => zipObject(columns, r)); + } + public query: (typeof AbstractDriver)['prototype']['query'] = async (queries, opt = {}) => { const irisdb = await this.open(); const listQueries = this.splitQueries(queries.toString()); const queriesResults = await Promise.all(listQueries.map(query => irisdb.query(query, []))); const resultsAgg: NSDatabase.IResult[] = []; queriesResults.forEach(queryResult => { - resultsAgg.push({ - cols: queryResult.length ? Object.keys(queryResult[0]) : [], - connId: this.getId(), - messages: [{ date: new Date(), message: `Query ok with ${queryResult.length} results` }], - results: queryResult, - query: queries.toString(), - requestId: opt.requestId, - resultId: generateId(), - }); + if (irisdb.apiVersion < 6) { + resultsAgg.push({ + cols: queryResult.content.length ? Object.keys(queryResult.content[0]) : [], + connId: this.getId(), + messages: [{ date: new Date(), message: `Query ok with ${queryResult.content.length} results` }], + results: queryResult.content, + query: queries.toString(), + requestId: opt.requestId, + resultId: generateId(), + }); + } + else { + const cols = this.getColumnNames(queryResult[0].columns || []); + resultsAgg.push({ + cols, + connId: this.getId(), + messages: [{ date: new Date(), message: `Query ok with ${queryResult[0]?.content.length ?? 'no'} results` }], + results: this.mapRows(queryResult[0]?.content, cols), + query: queries.toString(), + requestId: opt.requestId, + resultId: generateId(), + }); + } }); return resultsAgg; diff --git a/src/ls/irisdb.ts b/src/ls/irisdb.ts index 42ac901..47c52fe 100644 --- a/src/ls/irisdb.ts +++ b/src/ls/irisdb.ts @@ -27,7 +27,11 @@ export default class IRISdb { private config: IRISDirect; private cookies: string[] = []; - private apiVersion = 1; + private _apiVersion = 1; + + public get apiVersion() { + return this._apiVersion; + } public constructor(config: IRISDirect) { this.config = config; @@ -57,11 +61,11 @@ export default class IRISdb { headers?: any ): Promise { const { https, host, port, pathPrefix, username, password } = this.config; - if (minVersion > this.apiVersion) { - return Promise.reject(`${path} not supported by API version ${this.apiVersion}`); + if (minVersion > this._apiVersion) { + return Promise.reject(`${path} not supported by API version ${this._apiVersion}`); } if (minVersion && minVersion > 0) { - path = `v${this.apiVersion}/${path}`; + path = `v${this._apiVersion}/${path}`; } headers = { @@ -169,7 +173,7 @@ export default class IRISdb { You must select one of the following: ${data.namespaces.join(", ")}.`, }; } - this.apiVersion = data.api; + this._apiVersion = data.api; return info; } }); @@ -183,7 +187,7 @@ export default class IRISdb { return this.request(1, "POST", `${this.config.namespace}/action/query`, { parameters, query, - }).then(data => data.result.content) + }, this._apiVersion >= 6 ? { positional: true } : {}).then(data => data.result) }